@tanstack/react-router 0.0.1-beta.12 → 0.0.1-beta.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
3
  "author": "Tanner Linsley",
4
- "version": "0.0.1-beta.12",
4
+ "version": "0.0.1-beta.13",
5
5
  "license": "MIT",
6
6
  "repository": "tanstack/router",
7
7
  "homepage": "https://tanstack.com/router/",
@@ -41,7 +41,8 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@babel/runtime": "^7.16.7",
44
- "@tanstack/router-core": "0.0.1-beta.11",
44
+ "@tanstack/router-core": "0.0.1-beta.13",
45
+ "react-lazy-with-preload": "^2.2.1",
45
46
  "use-sync-external-store": "^1.2.0"
46
47
  },
47
48
  "devDependencies": {
package/src/index.tsx CHANGED
@@ -35,11 +35,19 @@ import {
35
35
 
36
36
  export * from '@tanstack/router-core'
37
37
 
38
+ export { lazyWithPreload as lazy } from 'react-lazy-with-preload/lib/index'
39
+ export type { PreloadableComponent as LazyComponent } from 'react-lazy-with-preload'
40
+
41
+ type SyncRouteComponent = (props?: {}) => React.ReactNode
42
+ export type RouteComponent = SyncRouteComponent & {
43
+ preload?: () => Promise<{
44
+ default: SyncRouteComponent
45
+ }>
46
+ }
47
+
38
48
  declare module '@tanstack/router-core' {
39
49
  interface FrameworkGenerics {
40
- Element: React.ReactNode
41
- // Any is required here so import() will work without having to do import().then(d => d.default)
42
- SyncOrAsyncElement: React.ReactNode | (() => Promise<any>)
50
+ Component: RouteComponent
43
51
  }
44
52
 
45
53
  interface Router<
@@ -262,6 +270,12 @@ export function createReactRouter<
262
270
  next,
263
271
  } = linkInfo
264
272
 
273
+ const reactHandleClick = (e: Event) => {
274
+ React.startTransition(() => {
275
+ handleClick(e)
276
+ })
277
+ }
278
+
265
279
  const composeHandlers =
266
280
  (handlers: (undefined | ((e: any) => void))[]) =>
267
281
  (e: React.SyntheticEvent) => {
@@ -284,7 +298,7 @@ export function createReactRouter<
284
298
  ...resolvedInactiveProps,
285
299
  ...rest,
286
300
  href: disabled ? undefined : next.href,
287
- onClick: composeHandlers([handleClick, onClick]),
301
+ onClick: composeHandlers([reactHandleClick, onClick]),
288
302
  onFocus: composeHandlers([handleFocus, onFocus]),
289
303
  onMouseEnter: composeHandlers([handleEnter, onMouseEnter]),
290
304
  onMouseLeave: composeHandlers([handleLeave, onMouseLeave]),
@@ -402,19 +416,13 @@ export function createReactRouter<
402
416
 
403
417
  Object.assign(route, routeExt)
404
418
  },
405
- createElement: async (element) => {
406
- if (typeof element === 'function') {
407
- const res = (await element()) as any
408
-
409
- // Support direct import() calls
410
- if (typeof res === 'object' && res.default) {
411
- return React.createElement(res.default)
412
- } else {
413
- return res
414
- }
419
+ loadComponent: async (component) => {
420
+ if (component.preload && typeof document !== 'undefined') {
421
+ component.preload()
422
+ // return await component.preload()
415
423
  }
416
424
 
417
- return element
425
+ return component as any
418
426
  },
419
427
  })
420
428
 
@@ -470,61 +478,54 @@ export function Outlet() {
470
478
  const matches = useMatches().slice(1)
471
479
  const match = matches[0]
472
480
 
481
+ const defaultPending = React.useCallback(() => null, [])
482
+
473
483
  if (!match) {
474
484
  return null
475
485
  }
476
486
 
477
- const element = ((): React.ReactNode => {
478
- if (!match) {
479
- return null
480
- }
481
-
482
- const errorElement =
483
- match.__.errorElement ?? router.options.defaultErrorElement
484
-
485
- if (match.status === 'error') {
486
- if (errorElement) {
487
- return errorElement as any
488
- }
489
-
490
- if (match.options.useErrorBoundary || router.options.useErrorBoundary) {
491
- throw match.error
492
- }
493
-
494
- return <DefaultErrorBoundary error={match.error} />
495
- }
496
-
497
- if (match.status === 'loading' || match.status === 'idle') {
498
- if (match.isPending) {
499
- const pendingElement =
500
- match.__.pendingElement ?? router.options.defaultPendingElement
487
+ const PendingComponent = (match.__.pendingComponent ??
488
+ router.options.defaultPendingComponent ??
489
+ defaultPending) as any
501
490
 
502
- if (match.options.pendingMs || pendingElement) {
503
- return (pendingElement as any) ?? null
504
- }
505
- }
491
+ const errorComponent =
492
+ match.__.errorComponent ?? router.options.defaultErrorComponent
506
493
 
507
- return null
508
- }
494
+ return (
495
+ <MatchesProvider value={matches}>
496
+ <React.Suspense fallback={<PendingComponent />}>
497
+ <CatchBoundary errorComponent={errorComponent}>
498
+ {
499
+ ((): React.ReactNode => {
500
+ if (match.status === 'error') {
501
+ throw match.error
502
+ }
509
503
 
510
- return (
511
- (match.__.element as any) ?? router.options.defaultElement ?? <Outlet />
512
- )
513
- })() as JSX.Element
504
+ if (match.status === 'success') {
505
+ return React.createElement(
506
+ (match.__.component as any) ??
507
+ router.options.defaultComponent ??
508
+ Outlet,
509
+ )
510
+ }
514
511
 
515
- const catchElement =
516
- match?.options.catchElement ?? router.options.defaultCatchElement
512
+ if (match.__.loadPromise) {
513
+ console.log(match.matchId, 'suspend')
514
+ throw match.__.loadPromise
515
+ }
517
516
 
518
- return (
519
- <MatchesProvider value={matches}>
520
- <CatchBoundary catchElement={catchElement}>{element}</CatchBoundary>
517
+ invariant(false, 'This should never happen!')
518
+ })() as JSX.Element
519
+ }
520
+ </CatchBoundary>
521
+ </React.Suspense>
521
522
  </MatchesProvider>
522
523
  )
523
524
  }
524
525
 
525
526
  class CatchBoundary extends React.Component<{
526
527
  children: any
527
- catchElement: any
528
+ errorComponent: any
528
529
  }> {
529
530
  state = {
530
531
  error: false,
@@ -538,12 +539,10 @@ class CatchBoundary extends React.Component<{
538
539
  })
539
540
  }
540
541
  render() {
541
- const catchElement = this.props.catchElement ?? DefaultErrorBoundary
542
+ const errorComponent = this.props.errorComponent ?? DefaultErrorBoundary
542
543
 
543
544
  if (this.state.error) {
544
- return typeof catchElement === 'function'
545
- ? catchElement(this.state)
546
- : catchElement
545
+ return React.createElement(errorComponent, this.state)
547
546
  }
548
547
 
549
548
  return this.props.children
@@ -572,19 +571,6 @@ export function DefaultErrorBoundary({ error }: { error: any }) {
572
571
  ) : null}
573
572
  </pre>
574
573
  </div>
575
- {/* <div style={{ height: '1rem' }} />
576
- <div
577
- style={{
578
- fontSize: '.8em',
579
- borderLeft: '3px solid rgba(127, 127, 127, 1)',
580
- paddingLeft: '.5rem',
581
- opacity: 0.5,
582
- }}
583
- >
584
- If you are the owner of this website, it's highly recommended that you
585
- configure your own custom Catch/Error boundaries for the router. You can
586
- optionally configure a boundary for each route.
587
- </div> */}
588
574
  </div>
589
575
  )
590
576
  }