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

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.14",
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.14",
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
 
@@ -437,10 +445,8 @@ export function RouterProvider<
437
445
  router.update(rest)
438
446
 
439
447
  useRouterSubscription(router)
440
- useLayoutEffect(() => {
441
- const unsub = router.mount()
442
- router.load()
443
- return unsub
448
+ React.useEffect(() => {
449
+ return router.mount()
444
450
  }, [router])
445
451
 
446
452
  return (
@@ -470,61 +476,54 @@ export function Outlet() {
470
476
  const matches = useMatches().slice(1)
471
477
  const match = matches[0]
472
478
 
479
+ const defaultPending = React.useCallback(() => null, [])
480
+
473
481
  if (!match) {
474
482
  return null
475
483
  }
476
484
 
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
- }
485
+ const PendingComponent = (match.__.pendingComponent ??
486
+ router.options.defaultPendingComponent ??
487
+ defaultPending) as any
489
488
 
490
- if (match.options.useErrorBoundary || router.options.useErrorBoundary) {
491
- throw match.error
492
- }
493
-
494
- return <DefaultErrorBoundary error={match.error} />
495
- }
489
+ const errorComponent =
490
+ match.__.errorComponent ?? router.options.defaultErrorComponent
496
491
 
497
- if (match.status === 'loading' || match.status === 'idle') {
498
- if (match.isPending) {
499
- const pendingElement =
500
- match.__.pendingElement ?? router.options.defaultPendingElement
501
-
502
- if (match.options.pendingMs || pendingElement) {
503
- return (pendingElement as any) ?? null
504
- }
505
- }
506
-
507
- return null
508
- }
492
+ return (
493
+ <MatchesProvider value={matches}>
494
+ <React.Suspense fallback={<PendingComponent />}>
495
+ <CatchBoundary errorComponent={errorComponent}>
496
+ {
497
+ ((): React.ReactNode => {
498
+ if (match.status === 'error') {
499
+ throw match.error
500
+ }
509
501
 
510
- return (
511
- (match.__.element as any) ?? router.options.defaultElement ?? <Outlet />
512
- )
513
- })() as JSX.Element
502
+ if (match.status === 'success') {
503
+ return React.createElement(
504
+ (match.__.component as any) ??
505
+ router.options.defaultComponent ??
506
+ Outlet,
507
+ )
508
+ }
514
509
 
515
- const catchElement =
516
- match?.options.catchElement ?? router.options.defaultCatchElement
510
+ if (match.__.loadPromise) {
511
+ console.log(match.matchId, 'suspend')
512
+ throw match.__.loadPromise
513
+ }
517
514
 
518
- return (
519
- <MatchesProvider value={matches}>
520
- <CatchBoundary catchElement={catchElement}>{element}</CatchBoundary>
515
+ invariant(false, 'This should never happen!')
516
+ })() as JSX.Element
517
+ }
518
+ </CatchBoundary>
519
+ </React.Suspense>
521
520
  </MatchesProvider>
522
521
  )
523
522
  }
524
523
 
525
524
  class CatchBoundary extends React.Component<{
526
525
  children: any
527
- catchElement: any
526
+ errorComponent: any
528
527
  }> {
529
528
  state = {
530
529
  error: false,
@@ -538,12 +537,10 @@ class CatchBoundary extends React.Component<{
538
537
  })
539
538
  }
540
539
  render() {
541
- const catchElement = this.props.catchElement ?? DefaultErrorBoundary
540
+ const errorComponent = this.props.errorComponent ?? DefaultErrorBoundary
542
541
 
543
542
  if (this.state.error) {
544
- return typeof catchElement === 'function'
545
- ? catchElement(this.state)
546
- : catchElement
543
+ return React.createElement(errorComponent, this.state)
547
544
  }
548
545
 
549
546
  return this.props.children
@@ -572,19 +569,6 @@ export function DefaultErrorBoundary({ error }: { error: any }) {
572
569
  ) : null}
573
570
  </pre>
574
571
  </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
572
  </div>
589
573
  )
590
574
  }