react-router 0.0.0-experimental-e192105b → 0.0.0-experimental-178bc9ee

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.
@@ -65,8 +65,10 @@ export interface PathRouteProps {
65
65
  index?: false;
66
66
  children?: React.ReactNode;
67
67
  element?: React.ReactNode | null;
68
+ fallbackElement?: React.ReactNode | null;
68
69
  errorElement?: React.ReactNode | null;
69
70
  Component?: React.ComponentType | null;
71
+ Fallback?: React.ComponentType | null;
70
72
  ErrorBoundary?: React.ComponentType | null;
71
73
  }
72
74
  export interface LayoutRouteProps extends PathRouteProps {
@@ -84,8 +86,10 @@ export interface IndexRouteProps {
84
86
  index: true;
85
87
  children?: undefined;
86
88
  element?: React.ReactNode | null;
89
+ fallbackElement?: React.ReactNode | null;
87
90
  errorElement?: React.ReactNode | null;
88
91
  Component?: React.ComponentType | null;
92
+ Fallback?: React.ComponentType | null;
89
93
  ErrorBoundary?: React.ComponentType | null;
90
94
  }
91
95
  export type RouteProps = PathRouteProps | LayoutRouteProps | IndexRouteProps;
@@ -12,8 +12,10 @@ export interface IndexRouteObject {
12
12
  index: true;
13
13
  children?: undefined;
14
14
  element?: React.ReactNode | null;
15
+ fallbackElement?: React.ReactNode | null;
15
16
  errorElement?: React.ReactNode | null;
16
17
  Component?: React.ComponentType | null;
18
+ Fallback?: React.ComponentType | null;
17
19
  ErrorBoundary?: React.ComponentType | null;
18
20
  lazy?: LazyRouteFunction<RouteObject>;
19
21
  }
@@ -29,8 +31,10 @@ export interface NonIndexRouteObject {
29
31
  index?: false;
30
32
  children?: RouteObject[];
31
33
  element?: React.ReactNode | null;
34
+ fallbackElement?: React.ReactNode | null;
32
35
  errorElement?: React.ReactNode | null;
33
36
  Component?: React.ComponentType | null;
37
+ Fallback?: React.ComponentType | null;
34
38
  ErrorBoundary?: React.ComponentType | null;
35
39
  lazy?: LazyRouteFunction<RouteObject>;
36
40
  }
@@ -96,7 +96,7 @@ export declare function useResolvedPath(to: To, { relative }?: {
96
96
  * @see https://reactrouter.com/hooks/use-routes
97
97
  */
98
98
  export declare function useRoutes(routes: RouteObject[], locationArg?: Partial<Location> | string): React.ReactElement | null;
99
- export declare function useRoutesImpl(routes: RouteObject[], locationArg?: Partial<Location> | string, dataRouterState?: RemixRouter["state"]): React.ReactElement | null;
99
+ export declare function useRoutesImpl(routes: RouteObject[], locationArg?: Partial<Location> | string, dataRouterState?: RemixRouter["state"], future?: RemixRouter["future"]): React.ReactElement | null;
100
100
  type RenderErrorBoundaryProps = React.PropsWithChildren<{
101
101
  location: Location;
102
102
  revalidation: RevalidationState;
@@ -122,7 +122,7 @@ export declare class RenderErrorBoundary extends React.Component<RenderErrorBoun
122
122
  componentDidCatch(error: any, errorInfo: any): void;
123
123
  render(): string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null | undefined;
124
124
  }
125
- export declare function _renderMatches(matches: RouteMatch[] | null, parentMatches?: RouteMatch[], dataRouterState?: RemixRouter["state"] | null): React.ReactElement | null;
125
+ export declare function _renderMatches(matches: RouteMatch[] | null, parentMatches?: RouteMatch[], dataRouterState?: RemixRouter["state"] | null, future?: RemixRouter["future"] | null): React.ReactElement | null;
126
126
  /**
127
127
  * Returns the ID for the nearest contextual route
128
128
  */
package/dist/main.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * React Router v0.0.0-experimental-e192105b
2
+ * React Router v0.0.0-experimental-178bc9ee
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * React Router v0.0.0-experimental-e192105b
2
+ * React Router v0.0.0-experimental-178bc9ee
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -9,7 +9,7 @@
9
9
  * @license MIT
10
10
  */
11
11
  import * as React from 'react';
12
- import { UNSAFE_invariant, joinPaths, matchPath, UNSAFE_getPathContributingMatches, UNSAFE_warning, resolveTo, parsePath, matchRoutes, Action, UNSAFE_convertRouteMatchToUiMatch, stripBasename, IDLE_BLOCKER, isRouteErrorResponse, createMemoryHistory, AbortedDeferredError, createRouter } from '@remix-run/router';
12
+ import { UNSAFE_invariant, joinPaths, matchPath, UNSAFE_getResolveToMatches, UNSAFE_warning, resolveTo, parsePath, matchRoutes, Action, UNSAFE_convertRouteMatchToUiMatch, stripBasename, IDLE_BLOCKER, isRouteErrorResponse, createMemoryHistory, AbortedDeferredError, createRouter } from '@remix-run/router';
13
13
  export { AbortedDeferredError, Action as NavigationType, createPath, defer, generatePath, isRouteErrorResponse, json, matchPath, matchRoutes, parsePath, redirect, redirectDocument, resolvePath } from '@remix-run/router';
14
14
 
15
15
  const DataRouterContext = /*#__PURE__*/React.createContext(null);
@@ -175,7 +175,7 @@ function useNavigateUnstable() {
175
175
  let {
176
176
  pathname: locationPathname
177
177
  } = useLocation();
178
- let routePathnamesJson = JSON.stringify(UNSAFE_getPathContributingMatches(matches).map(match => match.pathnameBase));
178
+ let routePathnamesJson = JSON.stringify(UNSAFE_getResolveToMatches(matches));
179
179
  let activeRef = React.useRef(false);
180
180
  useIsomorphicLayoutEffect(() => {
181
181
  activeRef.current = true;
@@ -254,9 +254,7 @@ function useResolvedPath(to, {
254
254
  let {
255
255
  pathname: locationPathname
256
256
  } = useLocation();
257
- // Use the full pathname for the leaf match so we include splat values
258
- // for "." links
259
- let routePathnamesJson = JSON.stringify(UNSAFE_getPathContributingMatches(matches).map((match, idx) => idx === matches.length - 1 ? match.pathname : match.pathnameBase));
257
+ let routePathnamesJson = JSON.stringify(UNSAFE_getResolveToMatches(matches));
260
258
  return React.useMemo(() => resolveTo(to, JSON.parse(routePathnamesJson), locationPathname, relative === "path"), [to, routePathnamesJson, locationPathname, relative]);
261
259
  }
262
260
  /**
@@ -271,7 +269,7 @@ function useRoutes(routes, locationArg) {
271
269
  return useRoutesImpl(routes, locationArg);
272
270
  }
273
271
  // Internal implementation with accept optional param for RouterProvider usage
274
- function useRoutesImpl(routes, locationArg, dataRouterState) {
272
+ function useRoutesImpl(routes, locationArg, dataRouterState, future) {
275
273
  !useInRouterContext() ? UNSAFE_invariant(false,
276
274
  // TODO: This error is probably because they somehow have 2 versions of the
277
275
  // router loaded. We can help them understand how to avoid that.
@@ -337,7 +335,7 @@ function useRoutesImpl(routes, locationArg, dataRouterState) {
337
335
  pathnameBase: match.pathnameBase === "/" ? parentPathnameBase : joinPaths([parentPathnameBase,
338
336
  // Re-encode pathnames that were decoded inside matchRoutes
339
337
  navigator.encodeLocation ? navigator.encodeLocation(match.pathnameBase).pathname : match.pathnameBase])
340
- })), parentMatches, dataRouterState);
338
+ })), parentMatches, dataRouterState, future);
341
339
  // When a user passes in a `locationArg`, the associated routes need to
342
340
  // be wrapped in a new `LocationContext.Provider` in order for `useLocation`
343
341
  // to use the scoped location instead of the global location.
@@ -456,7 +454,7 @@ function RenderedRoute({
456
454
  value: routeContext
457
455
  }, children);
458
456
  }
459
- function _renderMatches(matches, parentMatches = [], dataRouterState = null) {
457
+ function _renderMatches(matches, parentMatches = [], dataRouterState = null, future = null) {
460
458
  if (matches == null) {
461
459
  if (dataRouterState?.errors) {
462
460
  // Don't bail if we have data router errors so we can render them in the
@@ -474,18 +472,57 @@ function _renderMatches(matches, parentMatches = [], dataRouterState = null) {
474
472
  !(errorIndex >= 0) ? UNSAFE_invariant(false, `Could not find a matching route for errors on route IDs: ${Object.keys(errors).join(",")}`) : void 0;
475
473
  renderedMatches = renderedMatches.slice(0, Math.min(renderedMatches.length, errorIndex + 1));
476
474
  }
475
+ // If we're in a partial hydration mode, detect if we need to render down to
476
+ // a given Fallback while we load the rest of the hydration data
477
+ let renderFallback = false;
478
+ let fallbackIndex = -1;
479
+ if (dataRouterState && future && future.v7_partialHydration) {
480
+ for (let i = 0; i < renderedMatches.length; i++) {
481
+ let match = renderedMatches[i];
482
+ // Track the deepest fallback up until the first route without data
483
+ if (match.route.Fallback || match.route.fallbackElement) {
484
+ fallbackIndex = i;
485
+ }
486
+ if (match.route.loader && match.route.id && dataRouterState.loaderData[match.route.id] === undefined) {
487
+ // We found the first route without data, flag that we need to render
488
+ // a fallback and render up until the appropriate fallback
489
+ renderFallback = true;
490
+ if (fallbackIndex >= 0) {
491
+ renderedMatches = renderedMatches.slice(0, fallbackIndex + 1);
492
+ } else {
493
+ renderedMatches = [renderedMatches[0]];
494
+ }
495
+ break;
496
+ }
497
+ }
498
+ }
477
499
  return renderedMatches.reduceRight((outlet, match, index) => {
478
- let error = match.route.id ? errors?.[match.route.id] : null;
479
- // Only data routers handle errors
500
+ // Only data routers handle errors/fallbacks
501
+ let error;
502
+ let fallback = false;
480
503
  let errorElement = null;
504
+ let fallbackElement = null;
481
505
  if (dataRouterState) {
506
+ error = errors && match.route.id ? errors[match.route.id] : null;
482
507
  errorElement = match.route.errorElement || defaultErrorElement;
508
+ if (renderFallback) {
509
+ if (fallbackIndex < 0 && index === 0) {
510
+ warningOnce("route-fallback", false, "No `Fallback` element provided to render during initial hydration");
511
+ fallback = true;
512
+ fallbackElement = null;
513
+ } else if (fallbackIndex === index) {
514
+ fallback = true;
515
+ fallbackElement = match.route.fallbackElement || null;
516
+ }
517
+ }
483
518
  }
484
519
  let matches = parentMatches.concat(renderedMatches.slice(0, index + 1));
485
520
  let getChildren = () => {
486
521
  let children;
487
522
  if (error) {
488
523
  children = errorElement;
524
+ } else if (fallback) {
525
+ children = fallbackElement;
489
526
  } else if (match.route.Component) {
490
527
  // Note: This is a de-optimized path since React won't re-use the
491
528
  // ReactElement since it's identity changes with each new
@@ -631,9 +668,8 @@ function useRouteLoaderData(routeId) {
631
668
  */
632
669
  function useActionData() {
633
670
  let state = useDataRouterState(DataRouterStateHook.UseActionData);
634
- let route = React.useContext(RouteContext);
635
- !route ? UNSAFE_invariant(false, `useActionData must be used inside a RouteContext`) : void 0;
636
- return Object.values(state?.actionData || {})[0];
671
+ let routeId = useCurrentRouteId(DataRouterStateHook.UseLoaderData);
672
+ return state.actionData ? state.actionData[routeId] : undefined;
637
673
  }
638
674
  /**
639
675
  * Returns the nearest ancestor Route error, which could be a loader/action
@@ -809,6 +845,11 @@ function RouterProvider({
809
845
  // Need to use a layout effect here so we are subscribed early enough to
810
846
  // pick up on any render-driven redirects/navigations (useEffect/<Navigate>)
811
847
  React.useLayoutEffect(() => router.subscribe(setState), [router, setState]);
848
+ React.useEffect(() => {
849
+ UNSAFE_warning(fallbackElement == null || !router.future.v7_partialHydration, "`<RouterProvider fallbackElement>` is deprecated when using `v7_partialHydration`") ;
850
+ // Only log this once on initial mount
851
+ // eslint-disable-next-line react-hooks/exhaustive-deps
852
+ }, []);
812
853
  let navigator = React.useMemo(() => {
813
854
  return {
814
855
  createHref: router.createHref,
@@ -849,14 +890,16 @@ function RouterProvider({
849
890
  navigator: navigator
850
891
  }, state.initialized ? /*#__PURE__*/React.createElement(DataRoutes, {
851
892
  routes: router.routes,
893
+ future: router.future,
852
894
  state: state
853
895
  }) : fallbackElement))), null);
854
896
  }
855
897
  function DataRoutes({
856
898
  routes,
899
+ future,
857
900
  state
858
901
  }) {
859
- return useRoutesImpl(routes, undefined, state);
902
+ return useRoutesImpl(routes, undefined, state, future);
860
903
  }
861
904
  /**
862
905
  * A `<Router>` that stores all entries in memory.
@@ -927,7 +970,7 @@ function Navigate({
927
970
  let navigate = useNavigate();
928
971
  // Resolve the path outside of the effect so that when effects run twice in
929
972
  // StrictMode they navigate to the same place
930
- let path = resolveTo(to, UNSAFE_getPathContributingMatches(matches).map(match => match.pathnameBase), locationPathname, relative === "path");
973
+ let path = resolveTo(to, UNSAFE_getResolveToMatches(matches), locationPathname, relative === "path");
931
974
  let jsonPath = JSON.stringify(path);
932
975
  React.useEffect(() => navigate(JSON.parse(jsonPath), {
933
976
  replace,
@@ -1218,6 +1261,17 @@ function mapRouteProperties(route) {
1218
1261
  Component: undefined
1219
1262
  });
1220
1263
  }
1264
+ if (route.Fallback) {
1265
+ {
1266
+ if (route.fallbackElement) {
1267
+ UNSAFE_warning(false, "You should not include both `Fallback` and `fallbackElement` on your route - " + "`Fallback` will be used.") ;
1268
+ }
1269
+ }
1270
+ Object.assign(updates, {
1271
+ fallbackElement: /*#__PURE__*/React.createElement(route.Fallback),
1272
+ Fallback: undefined
1273
+ });
1274
+ }
1221
1275
  if (route.ErrorBoundary) {
1222
1276
  {
1223
1277
  if (route.errorElement) {