react-router 6.4.0-pre.8 → 6.4.0-pre.9

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/CHANGELOG.md CHANGED
@@ -1,5 +1,76 @@
1
1
  # react-router
2
2
 
3
+ ## 6.4.0-pre.9
4
+
5
+ ### Patch Changes
6
+
7
+ - Feat: adds `deferred` support to data routers (#9002)
8
+
9
+ Returning a `deferred` from a `loader` allows you to separate _critical_ loader data that you want to wait for prior to rendering the destination page from _non-critical_ data that you are OK to show a spinner for until it loads.
10
+
11
+ ```jsx
12
+ // In your route loader, return a deferred() and choose per-key whether to
13
+ // await the promise or not. As soon as the awaited promises resolve, the
14
+ // page will be rendered.
15
+ function loader() {
16
+ return deferred({
17
+ critical: await getCriticalData(),
18
+ lazy1: getLazyData(),
19
+ });
20
+ };
21
+
22
+ // In your route element, grab the values from useLoaderData and render them
23
+ // with <Deferred>
24
+ function DeferredPage() {
25
+ let data = useLoaderData();
26
+ return (
27
+ <>
28
+ <p>Critical Data: {data.critical}</p>
29
+ <Deferred
30
+ value={data.lazy}
31
+ fallback={<p>Loading...</p>}
32
+ errorElement={<RenderDeferredError />}>
33
+ <RenderDeferredData />
34
+ </Deferred>
35
+ </>
36
+ );
37
+ }
38
+
39
+ // Use separate components to render the data once it resolves, and access it
40
+ // via the useDeferredData hook
41
+ function RenderDeferredData() {
42
+ let data = useDeferredData();
43
+ return <p>Lazy: {data}</p>;
44
+ }
45
+
46
+ function RenderDeferredError() {
47
+ let data = useRouteError();
48
+ return <p>Error! {data.message} {data.stack}</p>;
49
+ }
50
+ ```
51
+
52
+ If you want to skip the separate components, you can use the Render Props
53
+ pattern and handle the rendering of the deferred data inline:
54
+
55
+ ```jsx
56
+ function DeferredPage() {
57
+ let data = useLoaderData();
58
+ return (
59
+ <>
60
+ <p>Critical Data: {data.critical}</p>
61
+ <Deferred value={data.lazy} fallback={<p>Loading...</p>}>
62
+ {(data) => <p>{data}</p>}
63
+ </Deferred>
64
+ </>
65
+ );
66
+ }
67
+ ```
68
+
69
+ - feat: add basename support for data routers (#9026)
70
+ - fix: Fix trailing slash behavior on pathless routing when using a basename (#9045)
71
+ - Updated dependencies
72
+ - @remix-run/router@0.2.0-pre.4
73
+
3
74
  ## 6.4.0-pre.8
4
75
 
5
76
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  import type { ActionFunction, ActionFunctionArgs, DataRouteMatch, Fetcher, JsonFunction, LoaderFunction, LoaderFunctionArgs, Location, Navigation, Params, ParamParseKey, Path, PathMatch, PathPattern, RedirectFunction, RouteMatch, RouteObject, ShouldRevalidateFunction, To } from "@remix-run/router";
2
- import { Action as NavigationType, createPath, generatePath, isRouteErrorResponse, json, matchPath, matchRoutes, parsePath, redirect, resolvePath } from "@remix-run/router";
3
- import type { DataMemoryRouterProps, MemoryRouterProps, NavigateProps, OutletProps, RouteProps, PathRouteProps, LayoutRouteProps, IndexRouteProps, RouterProps, RoutesProps } from "./lib/components";
4
- import { createRoutesFromChildren, renderMatches, MemoryRouter, DataMemoryRouter, Navigate, Outlet, Route, Router, Routes, useRenderDataRouter } from "./lib/components";
2
+ import { Action as NavigationType, createPath, deferred, generatePath, isDeferredError, isRouteErrorResponse, json, matchPath, matchRoutes, parsePath, redirect, resolvePath } from "@remix-run/router";
3
+ import type { DataMemoryRouterProps, DeferredProps, MemoryRouterProps, NavigateProps, OutletProps, RouteProps, PathRouteProps, LayoutRouteProps, IndexRouteProps, RouterProps, RoutesProps } from "./lib/components";
4
+ import { createRoutesFromChildren, renderMatches, DataMemoryRouter, Deferred, MemoryRouter, Navigate, Outlet, Route, Router, Routes, useRenderDataRouter } from "./lib/components";
5
5
  import type { Navigator, NavigateOptions } from "./lib/context";
6
6
  import { DataRouterContext, DataRouterStateContext, LocationContext, NavigationContext, RouteContext } from "./lib/context";
7
- import type { NavigateFunction } from "./lib/hooks";
8
- import { useHref, useInRouterContext, useLocation, useMatch, useNavigationType, useNavigate, useOutlet, useOutletContext, useParams, useResolvedPath, useRoutes, useActionData, useLoaderData, useMatches, useRouteLoaderData, useRouteError, useNavigation, useRevalidator } from "./lib/hooks";
7
+ import type { Deferrable, NavigateFunction } from "./lib/hooks";
8
+ import { useHref, useInRouterContext, useLocation, useMatch, useNavigationType, useNavigate, useOutlet, useOutletContext, useParams, useResolvedPath, useRoutes, useActionData, useDeferredData, useLoaderData, useMatches, useRouteLoaderData, useRouteError, useNavigation, useRevalidator } from "./lib/hooks";
9
9
  declare type Hash = string;
10
10
  declare type Pathname = string;
11
11
  declare type Search = string;
12
- export type { ActionFunction, ActionFunctionArgs, DataMemoryRouterProps, DataRouteMatch, Fetcher, Hash, IndexRouteProps, JsonFunction, LayoutRouteProps, LoaderFunction, LoaderFunctionArgs, Location, MemoryRouterProps, NavigateFunction, NavigateOptions, NavigateProps, Navigation, Navigator, OutletProps, Params, ParamParseKey, Path, PathMatch, Pathname, PathPattern, PathRouteProps, RedirectFunction, RouteMatch, RouteObject, RouteProps, RouterProps, RoutesProps, Search, ShouldRevalidateFunction, To, };
13
- export { DataMemoryRouter, MemoryRouter, Navigate, NavigationType, Outlet, Route, Router, Routes, createPath, createRoutesFromChildren, isRouteErrorResponse, generatePath, json, matchPath, matchRoutes, parsePath, redirect, renderMatches, resolvePath, useActionData, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes, };
12
+ export type { ActionFunction, ActionFunctionArgs, DataMemoryRouterProps, DataRouteMatch, Deferrable, DeferredProps, Fetcher, Hash, IndexRouteProps, JsonFunction, LayoutRouteProps, LoaderFunction, LoaderFunctionArgs, Location, MemoryRouterProps, NavigateFunction, NavigateOptions, NavigateProps, Navigation, Navigator, OutletProps, Params, ParamParseKey, Path, PathMatch, Pathname, PathPattern, PathRouteProps, RedirectFunction, RouteMatch, RouteObject, RouteProps, RouterProps, RoutesProps, Search, ShouldRevalidateFunction, To, };
13
+ export { DataMemoryRouter, Deferred, MemoryRouter, Navigate, NavigationType, Outlet, Route, Router, Routes, createPath, createRoutesFromChildren, deferred, isDeferredError, isRouteErrorResponse, generatePath, json, matchPath, matchRoutes, parsePath, redirect, renderMatches, resolvePath, useActionData, useDeferredData, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes, };
14
14
  /** @internal */
15
15
  export { NavigationContext as UNSAFE_NavigationContext, LocationContext as UNSAFE_LocationContext, RouteContext as UNSAFE_RouteContext, DataRouterContext as UNSAFE_DataRouterContext, DataRouterStateContext as UNSAFE_DataRouterStateContext, useRenderDataRouter, };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * React Router v6.4.0-pre.8
2
+ * React Router v6.4.0-pre.9
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -8,8 +8,8 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import { invariant, resolveTo, getToPathname, joinPaths, matchPath, warning, parsePath, matchRoutes, isRouteErrorResponse, createMemoryHistory, Action, stripBasename, createMemoryRouter } from '@remix-run/router';
12
- export { Action as NavigationType, createPath, generatePath, isRouteErrorResponse, json, matchPath, matchRoutes, parsePath, redirect, resolvePath } from '@remix-run/router';
11
+ import { invariant, resolveTo, joinPaths, matchPath, warning, parsePath, matchRoutes, isDeferredError, isRouteErrorResponse, createMemoryHistory, Action, stripBasename, createMemoryRouter } from '@remix-run/router';
12
+ export { Action as NavigationType, createPath, deferred, generatePath, isDeferredError, isRouteErrorResponse, json, matchPath, matchRoutes, parsePath, redirect, resolvePath } from '@remix-run/router';
13
13
  import * as React from 'react';
14
14
 
15
15
  /**
@@ -204,6 +204,12 @@ if (process.env.NODE_ENV !== "production") {
204
204
  DataRouterStateContext.displayName = "DataRouterState";
205
205
  }
206
206
 
207
+ const DeferredContext = /*#__PURE__*/React.createContext(null);
208
+
209
+ if (process.env.NODE_ENV !== "production") {
210
+ DeferredContext.displayName = "Deferred";
211
+ }
212
+
207
213
  const NavigationContext = /*#__PURE__*/React.createContext(null);
208
214
 
209
215
  if (process.env.NODE_ENV !== "production") {
@@ -251,22 +257,13 @@ function useHref(to) {
251
257
  pathname,
252
258
  search
253
259
  } = useResolvedPath(to);
254
- let joinedPathname = pathname;
260
+ let joinedPathname = pathname; // If we're operating within a basename, prepend it to the pathname prior
261
+ // to creating the href. If this is a root navigation, then just use the raw
262
+ // basename which allows the basename to have full control over the presence
263
+ // of a trailing slash on root links
255
264
 
256
265
  if (basename !== "/") {
257
- let toPathname = getToPathname(to); // Only append a trailing slash to the raw basename if the basename doesn't
258
- // already have one and this wasn't specifically a route to "". This
259
- // allows folks to control the trailing slash behavior when using a basename
260
-
261
- let appendSlash = !basename.endsWith("/") && to !== "" && (to == null ? void 0 : to.pathname) !== "" && toPathname != null && toPathname.endsWith("/");
262
-
263
- if (pathname !== "/") {
264
- joinedPathname = joinPaths([basename, pathname]);
265
- } else if (appendSlash) {
266
- joinedPathname = basename + "/";
267
- } else {
268
- joinedPathname = basename;
269
- }
266
+ joinedPathname = pathname === "/" ? basename : joinPaths([basename, pathname]);
270
267
  }
271
268
 
272
269
  return navigator.createHref({
@@ -396,14 +393,13 @@ function useNavigate() {
396
393
  return;
397
394
  }
398
395
 
399
- let path = resolveTo(to, JSON.parse(routePathnamesJson), locationPathname);
396
+ let path = resolveTo(to, JSON.parse(routePathnamesJson), locationPathname); // If we're operating within a basename, prepend it to the pathname prior
397
+ // to handing off to history. If this is a root navigation, then we
398
+ // navigate to the raw basename which allows the basename to have full
399
+ // control over the presence of a trailing slash on root links
400
400
 
401
401
  if (basename !== "/") {
402
- // If this was a blank path, just use the basename directly, this gives
403
- // the user control over trailing slash behavior
404
- let toPath = typeof to === "string" ? parsePath(to) : to;
405
- let isBlankPath = toPath.pathname == null || toPath.pathname === "";
406
- path.pathname = isBlankPath ? basename : joinPaths([basename, path.pathname]);
402
+ path.pathname = path.pathname === "/" ? basename : joinPaths([basename, path.pathname]);
407
403
  }
408
404
 
409
405
  (!!options.replace ? navigator.replace : navigator.push)(path, options.state, options);
@@ -781,9 +777,15 @@ function useRouteError() {
781
777
  let error = React.useContext(RouteErrorContext);
782
778
  let state = useDataRouterState(DataRouterHook.UseRouteError);
783
779
  let route = React.useContext(RouteContext);
784
- let thisRoute = route.matches[route.matches.length - 1]; // If this was a render error, we put it in a RouteError context inside
780
+ let thisRoute = route.matches[route.matches.length - 1];
781
+ let deferredValue = React.useContext(DeferredContext); // Return deferred errors if we're inside a Deferred errorElement
782
+
783
+ if (deferredValue && isDeferredError(deferredValue)) {
784
+ return deferredValue;
785
+ } // If this was a render error, we put it in a RouteError context inside
785
786
  // of RenderErrorBoundary
786
787
 
788
+
787
789
  if (error) {
788
790
  return error;
789
791
  }
@@ -793,6 +795,14 @@ function useRouteError() {
793
795
 
794
796
  return (_state$errors = state.errors) == null ? void 0 : _state$errors[thisRoute.route.id];
795
797
  }
798
+
799
+ /**
800
+ * Returns the happy-path data from the nearest ancestor <Deferred /> value
801
+ */
802
+ function useDeferredData() {
803
+ let value = React.useContext(DeferredContext);
804
+ return value;
805
+ }
796
806
  const alreadyWarned = {};
797
807
 
798
808
  function warningOnce(key, cond, message) {
@@ -813,6 +823,7 @@ let routerSingleton;
813
823
 
814
824
  function useRenderDataRouter(_ref) {
815
825
  let {
826
+ basename,
816
827
  children,
817
828
  fallbackElement,
818
829
  routes,
@@ -851,6 +862,7 @@ function useRenderDataRouter(_ref) {
851
862
  }, /*#__PURE__*/React.createElement(DataRouterStateContext.Provider, {
852
863
  value: state
853
864
  }, /*#__PURE__*/React.createElement(Router, {
865
+ basename: basename,
854
866
  location: state.location,
855
867
  navigationType: state.historyAction,
856
868
  navigator: navigator
@@ -861,6 +873,7 @@ function useRenderDataRouter(_ref) {
861
873
  }
862
874
  function DataMemoryRouter(_ref2) {
863
875
  let {
876
+ basename,
864
877
  children,
865
878
  initialEntries,
866
879
  initialIndex,
@@ -869,10 +882,12 @@ function DataMemoryRouter(_ref2) {
869
882
  routes
870
883
  } = _ref2;
871
884
  return useRenderDataRouter({
885
+ basename,
872
886
  children,
873
887
  fallbackElement,
874
888
  routes,
875
889
  createRouter: routes => createMemoryRouter({
890
+ basename,
876
891
  initialEntries,
877
892
  initialIndex,
878
893
  routes,
@@ -1063,6 +1078,68 @@ function DataRoutes(_ref7) {
1063
1078
  routes
1064
1079
  } = _ref7;
1065
1080
  return useRoutes(routes || createRoutesFromChildren(children), location);
1081
+ }
1082
+
1083
+ /**
1084
+ * Component to use for rendering lazily loaded data from returning deferred()
1085
+ * in a loader function
1086
+ */
1087
+ function Deferred(_ref8) {
1088
+ let {
1089
+ children,
1090
+ value,
1091
+ fallback,
1092
+ errorElement
1093
+ } = _ref8;
1094
+ return /*#__PURE__*/React.createElement(DeferredContext.Provider, {
1095
+ value: value
1096
+ }, /*#__PURE__*/React.createElement(React.Suspense, {
1097
+ fallback: fallback
1098
+ }, /*#__PURE__*/React.createElement(DeferredWrapper, {
1099
+ errorElement: errorElement
1100
+ }, typeof children === "function" ? /*#__PURE__*/React.createElement(ResolveDeferred, {
1101
+ children: children
1102
+ }) : children)));
1103
+ }
1104
+
1105
+ /**
1106
+ * @private
1107
+ * Internal wrapper to handle re-throwing the promise to trigger the Suspense
1108
+ * fallback, or rendering the children/errorElement once the promise resolves
1109
+ * or rejects
1110
+ */
1111
+ function DeferredWrapper(_ref9) {
1112
+ let {
1113
+ children,
1114
+ errorElement
1115
+ } = _ref9;
1116
+ let value = React.useContext(DeferredContext);
1117
+
1118
+ if (value instanceof Promise) {
1119
+ // throw to the suspense boundary
1120
+ throw value;
1121
+ }
1122
+
1123
+ if (isDeferredError(value)) {
1124
+ if (errorElement) {
1125
+ return /*#__PURE__*/React.createElement(React.Fragment, null, errorElement);
1126
+ } else {
1127
+ // Throw to the nearest route-level error boundary
1128
+ throw value;
1129
+ }
1130
+ }
1131
+
1132
+ return /*#__PURE__*/React.createElement(React.Fragment, null, children);
1133
+ }
1134
+
1135
+ /**
1136
+ * @private
1137
+ */
1138
+ function ResolveDeferred(_ref10) {
1139
+ let {
1140
+ children
1141
+ } = _ref10;
1142
+ return children(useDeferredData());
1066
1143
  } ///////////////////////////////////////////////////////////////////////////////
1067
1144
  // UTILS
1068
1145
  ///////////////////////////////////////////////////////////////////////////////
@@ -1075,7 +1152,6 @@ function DataRoutes(_ref7) {
1075
1152
  * @see https://reactrouter.com/docs/en/v6/utils/create-routes-from-children
1076
1153
  */
1077
1154
 
1078
-
1079
1155
  function createRoutesFromChildren(children, parentPath) {
1080
1156
  if (parentPath === void 0) {
1081
1157
  parentPath = [];
@@ -1126,5 +1202,5 @@ function renderMatches(matches) {
1126
1202
  return _renderMatches(matches);
1127
1203
  }
1128
1204
 
1129
- export { DataMemoryRouter, MemoryRouter, Navigate, Outlet, Route, Router, Routes, DataRouterContext as UNSAFE_DataRouterContext, DataRouterStateContext as UNSAFE_DataRouterStateContext, LocationContext as UNSAFE_LocationContext, NavigationContext as UNSAFE_NavigationContext, RouteContext as UNSAFE_RouteContext, createRoutesFromChildren, renderMatches, useActionData, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useRenderDataRouter, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes };
1205
+ export { DataMemoryRouter, Deferred, MemoryRouter, Navigate, Outlet, Route, Router, Routes, DataRouterContext as UNSAFE_DataRouterContext, DataRouterStateContext as UNSAFE_DataRouterStateContext, LocationContext as UNSAFE_LocationContext, NavigationContext as UNSAFE_NavigationContext, RouteContext as UNSAFE_RouteContext, createRoutesFromChildren, renderMatches, useActionData, useDeferredData, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useRenderDataRouter, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes };
1130
1206
  //# sourceMappingURL=index.js.map