UNPKG

48.6 kBJavaScriptView Raw
1/**
2 * React Router v6.16.0
3 *
4 * Copyright (c) Remix Software Inc.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE.md file in the root directory of this source tree.
8 *
9 * @license MIT
10 */
11import * as React from 'react';
12import { 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';
13export { AbortedDeferredError, Action as NavigationType, createPath, defer, generatePath, isRouteErrorResponse, json, matchPath, matchRoutes, parsePath, redirect, redirectDocument, resolvePath } from '@remix-run/router';
14
15const DataRouterContext = /*#__PURE__*/React.createContext(null);
16{
17 DataRouterContext.displayName = "DataRouter";
18}
19const DataRouterStateContext = /*#__PURE__*/React.createContext(null);
20{
21 DataRouterStateContext.displayName = "DataRouterState";
22}
23const AwaitContext = /*#__PURE__*/React.createContext(null);
24{
25 AwaitContext.displayName = "Await";
26}
27const NavigationContext = /*#__PURE__*/React.createContext(null);
28{
29 NavigationContext.displayName = "Navigation";
30}
31const LocationContext = /*#__PURE__*/React.createContext(null);
32{
33 LocationContext.displayName = "Location";
34}
35const RouteContext = /*#__PURE__*/React.createContext({
36 outlet: null,
37 matches: [],
38 isDataRoute: false
39});
40{
41 RouteContext.displayName = "Route";
42}
43const RouteErrorContext = /*#__PURE__*/React.createContext(null);
44{
45 RouteErrorContext.displayName = "RouteError";
46}
47
48/**
49 * Returns the full href for the given "to" value. This is useful for building
50 * custom links that are also accessible and preserve right-click behavior.
51 *
52 * @see https://reactrouter.com/hooks/use-href
53 */
54function useHref(to, {
55 relative
56} = {}) {
57 !useInRouterContext() ? UNSAFE_invariant(false,
58 // TODO: This error is probably because they somehow have 2 versions of the
59 // router loaded. We can help them understand how to avoid that.
60 `useHref() may be used only in the context of a <Router> component.`) : void 0;
61 let {
62 basename,
63 navigator
64 } = React.useContext(NavigationContext);
65 let {
66 hash,
67 pathname,
68 search
69 } = useResolvedPath(to, {
70 relative
71 });
72 let joinedPathname = pathname;
73 // If we're operating within a basename, prepend it to the pathname prior
74 // to creating the href. If this is a root navigation, then just use the raw
75 // basename which allows the basename to have full control over the presence
76 // of a trailing slash on root links
77 if (basename !== "/") {
78 joinedPathname = pathname === "/" ? basename : joinPaths([basename, pathname]);
79 }
80 return navigator.createHref({
81 pathname: joinedPathname,
82 search,
83 hash
84 });
85}
86/**
87 * Returns true if this component is a descendant of a <Router>.
88 *
89 * @see https://reactrouter.com/hooks/use-in-router-context
90 */
91function useInRouterContext() {
92 return React.useContext(LocationContext) != null;
93}
94/**
95 * Returns the current location object, which represents the current URL in web
96 * browsers.
97 *
98 * Note: If you're using this it may mean you're doing some of your own
99 * "routing" in your app, and we'd like to know what your use case is. We may
100 * be able to provide something higher-level to better suit your needs.
101 *
102 * @see https://reactrouter.com/hooks/use-location
103 */
104function useLocation() {
105 !useInRouterContext() ? UNSAFE_invariant(false,
106 // TODO: This error is probably because they somehow have 2 versions of the
107 // router loaded. We can help them understand how to avoid that.
108 `useLocation() may be used only in the context of a <Router> component.`) : void 0;
109 return React.useContext(LocationContext).location;
110}
111/**
112 * Returns the current navigation action which describes how the router came to
113 * the current location, either by a pop, push, or replace on the history stack.
114 *
115 * @see https://reactrouter.com/hooks/use-navigation-type
116 */
117function useNavigationType() {
118 return React.useContext(LocationContext).navigationType;
119}
120/**
121 * Returns a PathMatch object if the given pattern matches the current URL.
122 * This is useful for components that need to know "active" state, e.g.
123 * <NavLink>.
124 *
125 * @see https://reactrouter.com/hooks/use-match
126 */
127function useMatch(pattern) {
128 !useInRouterContext() ? UNSAFE_invariant(false,
129 // TODO: This error is probably because they somehow have 2 versions of the
130 // router loaded. We can help them understand how to avoid that.
131 `useMatch() may be used only in the context of a <Router> component.`) : void 0;
132 let {
133 pathname
134 } = useLocation();
135 return React.useMemo(() => matchPath(pattern, pathname), [pathname, pattern]);
136}
137const navigateEffectWarning = `You should call navigate() in a React.useEffect(), not when ` + `your component is first rendered.`;
138// Mute warnings for calls to useNavigate in SSR environments
139function useIsomorphicLayoutEffect(cb) {
140 let isStatic = React.useContext(NavigationContext).static;
141 if (!isStatic) {
142 // We should be able to get rid of this once react 18.3 is released
143 // See: https://github.com/facebook/react/pull/26395
144 // eslint-disable-next-line react-hooks/rules-of-hooks
145 React.useLayoutEffect(cb);
146 }
147}
148/**
149 * Returns an imperative method for changing the location. Used by <Link>s, but
150 * may also be used by other elements to change the location.
151 *
152 * @see https://reactrouter.com/hooks/use-navigate
153 */
154function useNavigate() {
155 let {
156 isDataRoute
157 } = React.useContext(RouteContext);
158 // Conditional usage is OK here because the usage of a data router is static
159 // eslint-disable-next-line react-hooks/rules-of-hooks
160 return isDataRoute ? useNavigateStable() : useNavigateUnstable();
161}
162function useNavigateUnstable() {
163 !useInRouterContext() ? UNSAFE_invariant(false,
164 // TODO: This error is probably because they somehow have 2 versions of the
165 // router loaded. We can help them understand how to avoid that.
166 `useNavigate() may be used only in the context of a <Router> component.`) : void 0;
167 let dataRouterContext = React.useContext(DataRouterContext);
168 let {
169 basename,
170 navigator
171 } = React.useContext(NavigationContext);
172 let {
173 matches
174 } = React.useContext(RouteContext);
175 let {
176 pathname: locationPathname
177 } = useLocation();
178 let routePathnamesJson = JSON.stringify(UNSAFE_getPathContributingMatches(matches).map(match => match.pathnameBase));
179 let activeRef = React.useRef(false);
180 useIsomorphicLayoutEffect(() => {
181 activeRef.current = true;
182 });
183 let navigate = React.useCallback((to, options = {}) => {
184 UNSAFE_warning(activeRef.current, navigateEffectWarning) ;
185 // Short circuit here since if this happens on first render the navigate
186 // is useless because we haven't wired up our history listener yet
187 if (!activeRef.current) return;
188 if (typeof to === "number") {
189 navigator.go(to);
190 return;
191 }
192 let path = resolveTo(to, JSON.parse(routePathnamesJson), locationPathname, options.relative === "path");
193 // If we're operating within a basename, prepend it to the pathname prior
194 // to handing off to history (but only if we're not in a data router,
195 // otherwise it'll prepend the basename inside of the router).
196 // If this is a root navigation, then we navigate to the raw basename
197 // which allows the basename to have full control over the presence of a
198 // trailing slash on root links
199 if (dataRouterContext == null && basename !== "/") {
200 path.pathname = path.pathname === "/" ? basename : joinPaths([basename, path.pathname]);
201 }
202 (!!options.replace ? navigator.replace : navigator.push)(path, options.state, options);
203 }, [basename, navigator, routePathnamesJson, locationPathname, dataRouterContext]);
204 return navigate;
205}
206const OutletContext = /*#__PURE__*/React.createContext(null);
207/**
208 * Returns the context (if provided) for the child route at this level of the route
209 * hierarchy.
210 * @see https://reactrouter.com/hooks/use-outlet-context
211 */
212function useOutletContext() {
213 return React.useContext(OutletContext);
214}
215/**
216 * Returns the element for the child route at this level of the route
217 * hierarchy. Used internally by <Outlet> to render child routes.
218 *
219 * @see https://reactrouter.com/hooks/use-outlet
220 */
221function useOutlet(context) {
222 let outlet = React.useContext(RouteContext).outlet;
223 if (outlet) {
224 return /*#__PURE__*/React.createElement(OutletContext.Provider, {
225 value: context
226 }, outlet);
227 }
228 return outlet;
229}
230/**
231 * Returns an object of key/value pairs of the dynamic params from the current
232 * URL that were matched by the route path.
233 *
234 * @see https://reactrouter.com/hooks/use-params
235 */
236function useParams() {
237 let {
238 matches
239 } = React.useContext(RouteContext);
240 let routeMatch = matches[matches.length - 1];
241 return routeMatch ? routeMatch.params : {};
242}
243/**
244 * Resolves the pathname of the given `to` value against the current location.
245 *
246 * @see https://reactrouter.com/hooks/use-resolved-path
247 */
248function useResolvedPath(to, {
249 relative
250} = {}) {
251 let {
252 matches
253 } = React.useContext(RouteContext);
254 let {
255 pathname: locationPathname
256 } = useLocation();
257 let routePathnamesJson = JSON.stringify(UNSAFE_getPathContributingMatches(matches).map(match => match.pathnameBase));
258 return React.useMemo(() => resolveTo(to, JSON.parse(routePathnamesJson), locationPathname, relative === "path"), [to, routePathnamesJson, locationPathname, relative]);
259}
260/**
261 * Returns the element of the route that matched the current location, prepared
262 * with the correct context to render the remainder of the route tree. Route
263 * elements in the tree must render an <Outlet> to render their child route's
264 * element.
265 *
266 * @see https://reactrouter.com/hooks/use-routes
267 */
268function useRoutes(routes, locationArg) {
269 return useRoutesImpl(routes, locationArg);
270}
271// Internal implementation with accept optional param for RouterProvider usage
272function useRoutesImpl(routes, locationArg, dataRouterState) {
273 !useInRouterContext() ? UNSAFE_invariant(false,
274 // TODO: This error is probably because they somehow have 2 versions of the
275 // router loaded. We can help them understand how to avoid that.
276 `useRoutes() may be used only in the context of a <Router> component.`) : void 0;
277 let {
278 navigator
279 } = React.useContext(NavigationContext);
280 let {
281 matches: parentMatches
282 } = React.useContext(RouteContext);
283 let routeMatch = parentMatches[parentMatches.length - 1];
284 let parentParams = routeMatch ? routeMatch.params : {};
285 let parentPathname = routeMatch ? routeMatch.pathname : "/";
286 let parentPathnameBase = routeMatch ? routeMatch.pathnameBase : "/";
287 let parentRoute = routeMatch && routeMatch.route;
288 {
289 // You won't get a warning about 2 different <Routes> under a <Route>
290 // without a trailing *, but this is a best-effort warning anyway since we
291 // cannot even give the warning unless they land at the parent route.
292 //
293 // Example:
294 //
295 // <Routes>
296 // {/* This route path MUST end with /* because otherwise
297 // it will never match /blog/post/123 */}
298 // <Route path="blog" element={<Blog />} />
299 // <Route path="blog/feed" element={<BlogFeed />} />
300 // </Routes>
301 //
302 // function Blog() {
303 // return (
304 // <Routes>
305 // <Route path="post/:id" element={<Post />} />
306 // </Routes>
307 // );
308 // }
309 let parentPath = parentRoute && parentRoute.path || "";
310 warningOnce(parentPathname, !parentRoute || parentPath.endsWith("*"), `You rendered descendant <Routes> (or called \`useRoutes()\`) at ` + `"${parentPathname}" (under <Route path="${parentPath}">) but the ` + `parent route path has no trailing "*". This means if you navigate ` + `deeper, the parent won't match anymore and therefore the child ` + `routes will never render.\n\n` + `Please change the parent <Route path="${parentPath}"> to <Route ` + `path="${parentPath === "/" ? "*" : `${parentPath}/*`}">.`);
311 }
312 let locationFromContext = useLocation();
313 let location;
314 if (locationArg) {
315 let parsedLocationArg = typeof locationArg === "string" ? parsePath(locationArg) : locationArg;
316 !(parentPathnameBase === "/" || parsedLocationArg.pathname?.startsWith(parentPathnameBase)) ? UNSAFE_invariant(false, `When overriding the location using \`<Routes location>\` or \`useRoutes(routes, location)\`, ` + `the location pathname must begin with the portion of the URL pathname that was ` + `matched by all parent routes. The current pathname base is "${parentPathnameBase}" ` + `but pathname "${parsedLocationArg.pathname}" was given in the \`location\` prop.`) : void 0;
317 location = parsedLocationArg;
318 } else {
319 location = locationFromContext;
320 }
321 let pathname = location.pathname || "/";
322 let remainingPathname = parentPathnameBase === "/" ? pathname : pathname.slice(parentPathnameBase.length) || "/";
323 let matches = matchRoutes(routes, {
324 pathname: remainingPathname
325 });
326 {
327 UNSAFE_warning(parentRoute || matches != null, `No routes matched location "${location.pathname}${location.search}${location.hash}" `) ;
328 UNSAFE_warning(matches == null || matches[matches.length - 1].route.element !== undefined || matches[matches.length - 1].route.Component !== undefined, `Matched leaf route at location "${location.pathname}${location.search}${location.hash}" ` + `does not have an element or Component. This means it will render an <Outlet /> with a ` + `null value by default resulting in an "empty" page.`) ;
329 }
330 let renderedMatches = _renderMatches(matches && matches.map(match => Object.assign({}, match, {
331 params: Object.assign({}, parentParams, match.params),
332 pathname: joinPaths([parentPathnameBase,
333 // Re-encode pathnames that were decoded inside matchRoutes
334 navigator.encodeLocation ? navigator.encodeLocation(match.pathname).pathname : match.pathname]),
335 pathnameBase: match.pathnameBase === "/" ? parentPathnameBase : joinPaths([parentPathnameBase,
336 // Re-encode pathnames that were decoded inside matchRoutes
337 navigator.encodeLocation ? navigator.encodeLocation(match.pathnameBase).pathname : match.pathnameBase])
338 })), parentMatches, dataRouterState);
339 // When a user passes in a `locationArg`, the associated routes need to
340 // be wrapped in a new `LocationContext.Provider` in order for `useLocation`
341 // to use the scoped location instead of the global location.
342 if (locationArg && renderedMatches) {
343 return /*#__PURE__*/React.createElement(LocationContext.Provider, {
344 value: {
345 location: {
346 pathname: "/",
347 search: "",
348 hash: "",
349 state: null,
350 key: "default",
351 ...location
352 },
353 navigationType: Action.Pop
354 }
355 }, renderedMatches);
356 }
357 return renderedMatches;
358}
359function DefaultErrorComponent() {
360 let error = useRouteError();
361 let message = isRouteErrorResponse(error) ? `${error.status} ${error.statusText}` : error instanceof Error ? error.message : JSON.stringify(error);
362 let stack = error instanceof Error ? error.stack : null;
363 let lightgrey = "rgba(200,200,200, 0.5)";
364 let preStyles = {
365 padding: "0.5rem",
366 backgroundColor: lightgrey
367 };
368 let codeStyles = {
369 padding: "2px 4px",
370 backgroundColor: lightgrey
371 };
372 let devInfo = null;
373 {
374 console.error("Error handled by React Router default ErrorBoundary:", error);
375 devInfo = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("p", null, "\uD83D\uDCBF Hey developer \uD83D\uDC4B"), /*#__PURE__*/React.createElement("p", null, "You can provide a way better UX than this when your app throws errors by providing your own ", /*#__PURE__*/React.createElement("code", {
376 style: codeStyles
377 }, "ErrorBoundary"), " or", " ", /*#__PURE__*/React.createElement("code", {
378 style: codeStyles
379 }, "errorElement"), " prop on your route."));
380 }
381 return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("h2", null, "Unexpected Application Error!"), /*#__PURE__*/React.createElement("h3", {
382 style: {
383 fontStyle: "italic"
384 }
385 }, message), stack ? /*#__PURE__*/React.createElement("pre", {
386 style: preStyles
387 }, stack) : null, devInfo);
388}
389const defaultErrorElement = /*#__PURE__*/React.createElement(DefaultErrorComponent, null);
390class RenderErrorBoundary extends React.Component {
391 constructor(props) {
392 super(props);
393 this.state = {
394 location: props.location,
395 revalidation: props.revalidation,
396 error: props.error
397 };
398 }
399 static getDerivedStateFromError(error) {
400 return {
401 error: error
402 };
403 }
404 static getDerivedStateFromProps(props, state) {
405 // When we get into an error state, the user will likely click "back" to the
406 // previous page that didn't have an error. Because this wraps the entire
407 // application, that will have no effect--the error page continues to display.
408 // This gives us a mechanism to recover from the error when the location changes.
409 //
410 // Whether we're in an error state or not, we update the location in state
411 // so that when we are in an error state, it gets reset when a new location
412 // comes in and the user recovers from the error.
413 if (state.location !== props.location || state.revalidation !== "idle" && props.revalidation === "idle") {
414 return {
415 error: props.error,
416 location: props.location,
417 revalidation: props.revalidation
418 };
419 }
420 // If we're not changing locations, preserve the location but still surface
421 // any new errors that may come through. We retain the existing error, we do
422 // this because the error provided from the app state may be cleared without
423 // the location changing.
424 return {
425 error: props.error || state.error,
426 location: state.location,
427 revalidation: props.revalidation || state.revalidation
428 };
429 }
430 componentDidCatch(error, errorInfo) {
431 console.error("React Router caught the following error during render", error, errorInfo);
432 }
433 render() {
434 return this.state.error ? /*#__PURE__*/React.createElement(RouteContext.Provider, {
435 value: this.props.routeContext
436 }, /*#__PURE__*/React.createElement(RouteErrorContext.Provider, {
437 value: this.state.error,
438 children: this.props.component
439 })) : this.props.children;
440 }
441}
442function RenderedRoute({
443 routeContext,
444 match,
445 children
446}) {
447 let dataRouterContext = React.useContext(DataRouterContext);
448 // Track how deep we got in our render pass to emulate SSR componentDidCatch
449 // in a DataStaticRouter
450 if (dataRouterContext && dataRouterContext.static && dataRouterContext.staticContext && (match.route.errorElement || match.route.ErrorBoundary)) {
451 dataRouterContext.staticContext._deepestRenderedBoundaryId = match.route.id;
452 }
453 return /*#__PURE__*/React.createElement(RouteContext.Provider, {
454 value: routeContext
455 }, children);
456}
457function _renderMatches(matches, parentMatches = [], dataRouterState = null) {
458 if (matches == null) {
459 if (dataRouterState?.errors) {
460 // Don't bail if we have data router errors so we can render them in the
461 // boundary. Use the pre-matched (or shimmed) matches
462 matches = dataRouterState.matches;
463 } else {
464 return null;
465 }
466 }
467 let renderedMatches = matches;
468 // If we have data errors, trim matches to the highest error boundary
469 let errors = dataRouterState?.errors;
470 if (errors != null) {
471 let errorIndex = renderedMatches.findIndex(m => m.route.id && errors?.[m.route.id]);
472 !(errorIndex >= 0) ? UNSAFE_invariant(false, `Could not find a matching route for errors on route IDs: ${Object.keys(errors).join(",")}`) : void 0;
473 renderedMatches = renderedMatches.slice(0, Math.min(renderedMatches.length, errorIndex + 1));
474 }
475 return renderedMatches.reduceRight((outlet, match, index) => {
476 let error = match.route.id ? errors?.[match.route.id] : null;
477 // Only data routers handle errors
478 let errorElement = null;
479 if (dataRouterState) {
480 errorElement = match.route.errorElement || defaultErrorElement;
481 }
482 let matches = parentMatches.concat(renderedMatches.slice(0, index + 1));
483 let getChildren = () => {
484 let children;
485 if (error) {
486 children = errorElement;
487 } else if (match.route.Component) {
488 // Note: This is a de-optimized path since React won't re-use the
489 // ReactElement since it's identity changes with each new
490 // React.createElement call. We keep this so folks can use
491 // `<Route Component={...}>` in `<Routes>` but generally `Component`
492 // usage is only advised in `RouterProvider` when we can convert it to
493 // `element` ahead of time.
494 children = /*#__PURE__*/React.createElement(match.route.Component, null);
495 } else if (match.route.element) {
496 children = match.route.element;
497 } else {
498 children = outlet;
499 }
500 return /*#__PURE__*/React.createElement(RenderedRoute, {
501 match: match,
502 routeContext: {
503 outlet,
504 matches,
505 isDataRoute: dataRouterState != null
506 },
507 children: children
508 });
509 };
510 // Only wrap in an error boundary within data router usages when we have an
511 // ErrorBoundary/errorElement on this route. Otherwise let it bubble up to
512 // an ancestor ErrorBoundary/errorElement
513 return dataRouterState && (match.route.ErrorBoundary || match.route.errorElement || index === 0) ? /*#__PURE__*/React.createElement(RenderErrorBoundary, {
514 location: dataRouterState.location,
515 revalidation: dataRouterState.revalidation,
516 component: errorElement,
517 error: error,
518 children: getChildren(),
519 routeContext: {
520 outlet: null,
521 matches,
522 isDataRoute: true
523 }
524 }) : getChildren();
525 }, null);
526}
527var DataRouterHook;
528(function (DataRouterHook) {
529 DataRouterHook["UseBlocker"] = "useBlocker";
530 DataRouterHook["UseRevalidator"] = "useRevalidator";
531 DataRouterHook["UseNavigateStable"] = "useNavigate";
532})(DataRouterHook || (DataRouterHook = {}));
533var DataRouterStateHook;
534(function (DataRouterStateHook) {
535 DataRouterStateHook["UseBlocker"] = "useBlocker";
536 DataRouterStateHook["UseLoaderData"] = "useLoaderData";
537 DataRouterStateHook["UseActionData"] = "useActionData";
538 DataRouterStateHook["UseRouteError"] = "useRouteError";
539 DataRouterStateHook["UseNavigation"] = "useNavigation";
540 DataRouterStateHook["UseRouteLoaderData"] = "useRouteLoaderData";
541 DataRouterStateHook["UseMatches"] = "useMatches";
542 DataRouterStateHook["UseRevalidator"] = "useRevalidator";
543 DataRouterStateHook["UseNavigateStable"] = "useNavigate";
544 DataRouterStateHook["UseRouteId"] = "useRouteId";
545})(DataRouterStateHook || (DataRouterStateHook = {}));
546function getDataRouterConsoleError(hookName) {
547 return `${hookName} must be used within a data router. See https://reactrouter.com/routers/picking-a-router.`;
548}
549function useDataRouterContext(hookName) {
550 let ctx = React.useContext(DataRouterContext);
551 !ctx ? UNSAFE_invariant(false, getDataRouterConsoleError(hookName)) : void 0;
552 return ctx;
553}
554function useDataRouterState(hookName) {
555 let state = React.useContext(DataRouterStateContext);
556 !state ? UNSAFE_invariant(false, getDataRouterConsoleError(hookName)) : void 0;
557 return state;
558}
559function useRouteContext(hookName) {
560 let route = React.useContext(RouteContext);
561 !route ? UNSAFE_invariant(false, getDataRouterConsoleError(hookName)) : void 0;
562 return route;
563}
564// Internal version with hookName-aware debugging
565function useCurrentRouteId(hookName) {
566 let route = useRouteContext(hookName);
567 let thisRoute = route.matches[route.matches.length - 1];
568 !thisRoute.route.id ? UNSAFE_invariant(false, `${hookName} can only be used on routes that contain a unique "id"`) : void 0;
569 return thisRoute.route.id;
570}
571/**
572 * Returns the ID for the nearest contextual route
573 */
574function useRouteId() {
575 return useCurrentRouteId(DataRouterStateHook.UseRouteId);
576}
577/**
578 * Returns the current navigation, defaulting to an "idle" navigation when
579 * no navigation is in progress
580 */
581function useNavigation() {
582 let state = useDataRouterState(DataRouterStateHook.UseNavigation);
583 return state.navigation;
584}
585/**
586 * Returns a revalidate function for manually triggering revalidation, as well
587 * as the current state of any manual revalidations
588 */
589function useRevalidator() {
590 let dataRouterContext = useDataRouterContext(DataRouterHook.UseRevalidator);
591 let state = useDataRouterState(DataRouterStateHook.UseRevalidator);
592 return React.useMemo(() => ({
593 revalidate: dataRouterContext.router.revalidate,
594 state: state.revalidation
595 }), [dataRouterContext.router.revalidate, state.revalidation]);
596}
597/**
598 * Returns the active route matches, useful for accessing loaderData for
599 * parent/child routes or the route "handle" property
600 */
601function useMatches() {
602 let {
603 matches,
604 loaderData
605 } = useDataRouterState(DataRouterStateHook.UseMatches);
606 return React.useMemo(() => matches.map(m => UNSAFE_convertRouteMatchToUiMatch(m, loaderData)), [matches, loaderData]);
607}
608/**
609 * Returns the loader data for the nearest ancestor Route loader
610 */
611function useLoaderData() {
612 let state = useDataRouterState(DataRouterStateHook.UseLoaderData);
613 let routeId = useCurrentRouteId(DataRouterStateHook.UseLoaderData);
614 if (state.errors && state.errors[routeId] != null) {
615 console.error(`You cannot \`useLoaderData\` in an errorElement (routeId: ${routeId})`);
616 return undefined;
617 }
618 return state.loaderData[routeId];
619}
620/**
621 * Returns the loaderData for the given routeId
622 */
623function useRouteLoaderData(routeId) {
624 let state = useDataRouterState(DataRouterStateHook.UseRouteLoaderData);
625 return state.loaderData[routeId];
626}
627/**
628 * Returns the action data for the nearest ancestor Route action
629 */
630function useActionData() {
631 let state = useDataRouterState(DataRouterStateHook.UseActionData);
632 let route = React.useContext(RouteContext);
633 !route ? UNSAFE_invariant(false, `useActionData must be used inside a RouteContext`) : void 0;
634 return Object.values(state?.actionData || {})[0];
635}
636/**
637 * Returns the nearest ancestor Route error, which could be a loader/action
638 * error or a render error. This is intended to be called from your
639 * ErrorBoundary/errorElement to display a proper error message.
640 */
641function useRouteError() {
642 let error = React.useContext(RouteErrorContext);
643 let state = useDataRouterState(DataRouterStateHook.UseRouteError);
644 let routeId = useCurrentRouteId(DataRouterStateHook.UseRouteError);
645 // If this was a render error, we put it in a RouteError context inside
646 // of RenderErrorBoundary
647 if (error) {
648 return error;
649 }
650 // Otherwise look for errors from our data router state
651 return state.errors?.[routeId];
652}
653/**
654 * Returns the happy-path data from the nearest ancestor <Await /> value
655 */
656function useAsyncValue() {
657 let value = React.useContext(AwaitContext);
658 return value?._data;
659}
660/**
661 * Returns the error from the nearest ancestor <Await /> value
662 */
663function useAsyncError() {
664 let value = React.useContext(AwaitContext);
665 return value?._error;
666}
667let blockerId = 0;
668/**
669 * Allow the application to block navigations within the SPA and present the
670 * user a confirmation dialog to confirm the navigation. Mostly used to avoid
671 * using half-filled form data. This does not handle hard-reloads or
672 * cross-origin navigations.
673 */
674function useBlocker(shouldBlock) {
675 let {
676 router,
677 basename
678 } = useDataRouterContext(DataRouterHook.UseBlocker);
679 let state = useDataRouterState(DataRouterStateHook.UseBlocker);
680 let [blockerKey, setBlockerKey] = React.useState("");
681 let blockerFunction = React.useCallback(arg => {
682 if (typeof shouldBlock !== "function") {
683 return !!shouldBlock;
684 }
685 if (basename === "/") {
686 return shouldBlock(arg);
687 }
688 // If they provided us a function and we've got an active basename, strip
689 // it from the locations we expose to the user to match the behavior of
690 // useLocation
691 let {
692 currentLocation,
693 nextLocation,
694 historyAction
695 } = arg;
696 return shouldBlock({
697 currentLocation: {
698 ...currentLocation,
699 pathname: stripBasename(currentLocation.pathname, basename) || currentLocation.pathname
700 },
701 nextLocation: {
702 ...nextLocation,
703 pathname: stripBasename(nextLocation.pathname, basename) || nextLocation.pathname
704 },
705 historyAction
706 });
707 }, [basename, shouldBlock]);
708 // This effect is in charge of blocker key assignment and deletion (which is
709 // tightly coupled to the key)
710 React.useEffect(() => {
711 let key = String(++blockerId);
712 setBlockerKey(key);
713 return () => router.deleteBlocker(key);
714 }, [router]);
715 // This effect handles assigning the blockerFunction. This is to handle
716 // unstable blocker function identities, and happens only after the prior
717 // effect so we don't get an orphaned blockerFunction in the router with a
718 // key of "". Until then we just have the IDLE_BLOCKER.
719 React.useEffect(() => {
720 if (blockerKey !== "") {
721 router.getBlocker(blockerKey, blockerFunction);
722 }
723 }, [router, blockerKey, blockerFunction]);
724 // Prefer the blocker from `state` not `router.state` since DataRouterContext
725 // is memoized so this ensures we update on blocker state updates
726 return blockerKey && state.blockers.has(blockerKey) ? state.blockers.get(blockerKey) : IDLE_BLOCKER;
727}
728/**
729 * Stable version of useNavigate that is used when we are in the context of
730 * a RouterProvider.
731 */
732function useNavigateStable() {
733 let {
734 router
735 } = useDataRouterContext(DataRouterHook.UseNavigateStable);
736 let id = useCurrentRouteId(DataRouterStateHook.UseNavigateStable);
737 let activeRef = React.useRef(false);
738 useIsomorphicLayoutEffect(() => {
739 activeRef.current = true;
740 });
741 let navigate = React.useCallback((to, options = {}) => {
742 UNSAFE_warning(activeRef.current, navigateEffectWarning) ;
743 // Short circuit here since if this happens on first render the navigate
744 // is useless because we haven't wired up our router subscriber yet
745 if (!activeRef.current) return;
746 if (typeof to === "number") {
747 router.navigate(to);
748 } else {
749 router.navigate(to, {
750 fromRouteId: id,
751 ...options
752 });
753 }
754 }, [router, id]);
755 return navigate;
756}
757const alreadyWarned = {};
758function warningOnce(key, cond, message) {
759 if (!cond && !alreadyWarned[key]) {
760 alreadyWarned[key] = true;
761 UNSAFE_warning(false, message) ;
762 }
763}
764
765/**
766 Webpack + React 17 fails to compile on any of the following because webpack
767 complains that `startTransition` doesn't exist in `React`:
768 * import { startTransition } from "react"
769 * import * as React from from "react";
770 "startTransition" in React ? React.startTransition(() => setState()) : setState()
771 * import * as React from from "react";
772 "startTransition" in React ? React["startTransition"](() => setState()) : setState()
773
774 Moving it to a constant such as the following solves the Webpack/React 17 issue:
775 * import * as React from from "react";
776 const START_TRANSITION = "startTransition";
777 START_TRANSITION in React ? React[START_TRANSITION](() => setState()) : setState()
778
779 However, that introduces webpack/terser minification issues in production builds
780 in React 18 where minification/obfuscation ends up removing the call of
781 React.startTransition entirely from the first half of the ternary. Grabbing
782 this exported reference once up front resolves that issue.
783
784 See https://github.com/remix-run/react-router/issues/10579
785*/
786const START_TRANSITION = "startTransition";
787const startTransitionImpl = React[START_TRANSITION];
788/**
789 * Given a Remix Router instance, render the appropriate UI
790 */
791function RouterProvider({
792 fallbackElement,
793 router,
794 future
795}) {
796 // Need to use a layout effect here so we are subscribed early enough to
797 // pick up on any render-driven redirects/navigations (useEffect/<Navigate>)
798 let [state, setStateImpl] = React.useState(router.state);
799 let {
800 v7_startTransition
801 } = future || {};
802 let setState = React.useCallback(newState => {
803 v7_startTransition && startTransitionImpl ? startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState);
804 }, [setStateImpl, v7_startTransition]);
805 React.useLayoutEffect(() => router.subscribe(setState), [router, setState]);
806 let navigator = React.useMemo(() => {
807 return {
808 createHref: router.createHref,
809 encodeLocation: router.encodeLocation,
810 go: n => router.navigate(n),
811 push: (to, state, opts) => router.navigate(to, {
812 state,
813 preventScrollReset: opts?.preventScrollReset
814 }),
815 replace: (to, state, opts) => router.navigate(to, {
816 replace: true,
817 state,
818 preventScrollReset: opts?.preventScrollReset
819 })
820 };
821 }, [router]);
822 let basename = router.basename || "/";
823 let dataRouterContext = React.useMemo(() => ({
824 router,
825 navigator,
826 static: false,
827 basename
828 }), [router, navigator, basename]);
829 // The fragment and {null} here are important! We need them to keep React 18's
830 // useId happy when we are server-rendering since we may have a <script> here
831 // containing the hydrated server-side staticContext (from StaticRouterProvider).
832 // useId relies on the component tree structure to generate deterministic id's
833 // so we need to ensure it remains the same on the client even though
834 // we don't need the <script> tag
835 return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(DataRouterContext.Provider, {
836 value: dataRouterContext
837 }, /*#__PURE__*/React.createElement(DataRouterStateContext.Provider, {
838 value: state
839 }, /*#__PURE__*/React.createElement(Router, {
840 basename: basename,
841 location: state.location,
842 navigationType: state.historyAction,
843 navigator: navigator
844 }, state.initialized ? /*#__PURE__*/React.createElement(DataRoutes, {
845 routes: router.routes,
846 state: state
847 }) : fallbackElement))), null);
848}
849function DataRoutes({
850 routes,
851 state
852}) {
853 return useRoutesImpl(routes, undefined, state);
854}
855/**
856 * A <Router> that stores all entries in memory.
857 *
858 * @see https://reactrouter.com/router-components/memory-router
859 */
860function MemoryRouter({
861 basename,
862 children,
863 initialEntries,
864 initialIndex,
865 future
866}) {
867 let historyRef = React.useRef();
868 if (historyRef.current == null) {
869 historyRef.current = createMemoryHistory({
870 initialEntries,
871 initialIndex,
872 v5Compat: true
873 });
874 }
875 let history = historyRef.current;
876 let [state, setStateImpl] = React.useState({
877 action: history.action,
878 location: history.location
879 });
880 let {
881 v7_startTransition
882 } = future || {};
883 let setState = React.useCallback(newState => {
884 v7_startTransition && startTransitionImpl ? startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState);
885 }, [setStateImpl, v7_startTransition]);
886 React.useLayoutEffect(() => history.listen(setState), [history, setState]);
887 return /*#__PURE__*/React.createElement(Router, {
888 basename: basename,
889 children: children,
890 location: state.location,
891 navigationType: state.action,
892 navigator: history
893 });
894}
895/**
896 * Changes the current location.
897 *
898 * Note: This API is mostly useful in React.Component subclasses that are not
899 * able to use hooks. In functional components, we recommend you use the
900 * `useNavigate` hook instead.
901 *
902 * @see https://reactrouter.com/components/navigate
903 */
904function Navigate({
905 to,
906 replace,
907 state,
908 relative
909}) {
910 !useInRouterContext() ? UNSAFE_invariant(false,
911 // TODO: This error is probably because they somehow have 2 versions of
912 // the router loaded. We can help them understand how to avoid that.
913 `<Navigate> may be used only in the context of a <Router> component.`) : void 0;
914 UNSAFE_warning(!React.useContext(NavigationContext).static, `<Navigate> must not be used on the initial render in a <StaticRouter>. ` + `This is a no-op, but you should modify your code so the <Navigate> is ` + `only ever rendered in response to some user interaction or state change.`) ;
915 let {
916 matches
917 } = React.useContext(RouteContext);
918 let {
919 pathname: locationPathname
920 } = useLocation();
921 let navigate = useNavigate();
922 // Resolve the path outside of the effect so that when effects run twice in
923 // StrictMode they navigate to the same place
924 let path = resolveTo(to, UNSAFE_getPathContributingMatches(matches).map(match => match.pathnameBase), locationPathname, relative === "path");
925 let jsonPath = JSON.stringify(path);
926 React.useEffect(() => navigate(JSON.parse(jsonPath), {
927 replace,
928 state,
929 relative
930 }), [navigate, jsonPath, relative, replace, state]);
931 return null;
932}
933/**
934 * Renders the child route's element, if there is one.
935 *
936 * @see https://reactrouter.com/components/outlet
937 */
938function Outlet(props) {
939 return useOutlet(props.context);
940}
941/**
942 * Declares an element that should be rendered at a certain URL path.
943 *
944 * @see https://reactrouter.com/components/route
945 */
946function Route(_props) {
947 UNSAFE_invariant(false, `A <Route> is only ever to be used as the child of <Routes> element, ` + `never rendered directly. Please wrap your <Route> in a <Routes>.`) ;
948}
949/**
950 * Provides location context for the rest of the app.
951 *
952 * Note: You usually won't render a <Router> directly. Instead, you'll render a
953 * router that is more specific to your environment such as a <BrowserRouter>
954 * in web browsers or a <StaticRouter> for server rendering.
955 *
956 * @see https://reactrouter.com/router-components/router
957 */
958function Router({
959 basename: basenameProp = "/",
960 children = null,
961 location: locationProp,
962 navigationType = Action.Pop,
963 navigator,
964 static: staticProp = false
965}) {
966 !!useInRouterContext() ? UNSAFE_invariant(false, `You cannot render a <Router> inside another <Router>.` + ` You should never have more than one in your app.`) : void 0;
967 // Preserve trailing slashes on basename, so we can let the user control
968 // the enforcement of trailing slashes throughout the app
969 let basename = basenameProp.replace(/^\/*/, "/");
970 let navigationContext = React.useMemo(() => ({
971 basename,
972 navigator,
973 static: staticProp
974 }), [basename, navigator, staticProp]);
975 if (typeof locationProp === "string") {
976 locationProp = parsePath(locationProp);
977 }
978 let {
979 pathname = "/",
980 search = "",
981 hash = "",
982 state = null,
983 key = "default"
984 } = locationProp;
985 let locationContext = React.useMemo(() => {
986 let trailingPathname = stripBasename(pathname, basename);
987 if (trailingPathname == null) {
988 return null;
989 }
990 return {
991 location: {
992 pathname: trailingPathname,
993 search,
994 hash,
995 state,
996 key
997 },
998 navigationType
999 };
1000 }, [basename, pathname, search, hash, state, key, navigationType]);
1001 UNSAFE_warning(locationContext != null, `<Router basename="${basename}"> is not able to match the URL ` + `"${pathname}${search}${hash}" because it does not start with the ` + `basename, so the <Router> won't render anything.`) ;
1002 if (locationContext == null) {
1003 return null;
1004 }
1005 return /*#__PURE__*/React.createElement(NavigationContext.Provider, {
1006 value: navigationContext
1007 }, /*#__PURE__*/React.createElement(LocationContext.Provider, {
1008 children: children,
1009 value: locationContext
1010 }));
1011}
1012/**
1013 * A container for a nested tree of <Route> elements that renders the branch
1014 * that best matches the current location.
1015 *
1016 * @see https://reactrouter.com/components/routes
1017 */
1018function Routes({
1019 children,
1020 location
1021}) {
1022 return useRoutes(createRoutesFromChildren(children), location);
1023}
1024/**
1025 * Component to use for rendering lazily loaded data from returning defer()
1026 * in a loader function
1027 */
1028function Await({
1029 children,
1030 errorElement,
1031 resolve
1032}) {
1033 return /*#__PURE__*/React.createElement(AwaitErrorBoundary, {
1034 resolve: resolve,
1035 errorElement: errorElement
1036 }, /*#__PURE__*/React.createElement(ResolveAwait, null, children));
1037}
1038var AwaitRenderStatus;
1039(function (AwaitRenderStatus) {
1040 AwaitRenderStatus[AwaitRenderStatus["pending"] = 0] = "pending";
1041 AwaitRenderStatus[AwaitRenderStatus["success"] = 1] = "success";
1042 AwaitRenderStatus[AwaitRenderStatus["error"] = 2] = "error";
1043})(AwaitRenderStatus || (AwaitRenderStatus = {}));
1044const neverSettledPromise = new Promise(() => {});
1045class AwaitErrorBoundary extends React.Component {
1046 constructor(props) {
1047 super(props);
1048 this.state = {
1049 error: null
1050 };
1051 }
1052 static getDerivedStateFromError(error) {
1053 return {
1054 error
1055 };
1056 }
1057 componentDidCatch(error, errorInfo) {
1058 console.error("<Await> caught the following error during render", error, errorInfo);
1059 }
1060 render() {
1061 let {
1062 children,
1063 errorElement,
1064 resolve
1065 } = this.props;
1066 let promise = null;
1067 let status = AwaitRenderStatus.pending;
1068 if (!(resolve instanceof Promise)) {
1069 // Didn't get a promise - provide as a resolved promise
1070 status = AwaitRenderStatus.success;
1071 promise = Promise.resolve();
1072 Object.defineProperty(promise, "_tracked", {
1073 get: () => true
1074 });
1075 Object.defineProperty(promise, "_data", {
1076 get: () => resolve
1077 });
1078 } else if (this.state.error) {
1079 // Caught a render error, provide it as a rejected promise
1080 status = AwaitRenderStatus.error;
1081 let renderError = this.state.error;
1082 promise = Promise.reject().catch(() => {}); // Avoid unhandled rejection warnings
1083 Object.defineProperty(promise, "_tracked", {
1084 get: () => true
1085 });
1086 Object.defineProperty(promise, "_error", {
1087 get: () => renderError
1088 });
1089 } else if (resolve._tracked) {
1090 // Already tracked promise - check contents
1091 promise = resolve;
1092 status = promise._error !== undefined ? AwaitRenderStatus.error : promise._data !== undefined ? AwaitRenderStatus.success : AwaitRenderStatus.pending;
1093 } else {
1094 // Raw (untracked) promise - track it
1095 status = AwaitRenderStatus.pending;
1096 Object.defineProperty(resolve, "_tracked", {
1097 get: () => true
1098 });
1099 promise = resolve.then(data => Object.defineProperty(resolve, "_data", {
1100 get: () => data
1101 }), error => Object.defineProperty(resolve, "_error", {
1102 get: () => error
1103 }));
1104 }
1105 if (status === AwaitRenderStatus.error && promise._error instanceof AbortedDeferredError) {
1106 // Freeze the UI by throwing a never resolved promise
1107 throw neverSettledPromise;
1108 }
1109 if (status === AwaitRenderStatus.error && !errorElement) {
1110 // No errorElement, throw to the nearest route-level error boundary
1111 throw promise._error;
1112 }
1113 if (status === AwaitRenderStatus.error) {
1114 // Render via our errorElement
1115 return /*#__PURE__*/React.createElement(AwaitContext.Provider, {
1116 value: promise,
1117 children: errorElement
1118 });
1119 }
1120 if (status === AwaitRenderStatus.success) {
1121 // Render children with resolved value
1122 return /*#__PURE__*/React.createElement(AwaitContext.Provider, {
1123 value: promise,
1124 children: children
1125 });
1126 }
1127 // Throw to the suspense boundary
1128 throw promise;
1129 }
1130}
1131/**
1132 * @private
1133 * Indirection to leverage useAsyncValue for a render-prop API on <Await>
1134 */
1135function ResolveAwait({
1136 children
1137}) {
1138 let data = useAsyncValue();
1139 let toRender = typeof children === "function" ? children(data) : children;
1140 return /*#__PURE__*/React.createElement(React.Fragment, null, toRender);
1141}
1142///////////////////////////////////////////////////////////////////////////////
1143// UTILS
1144///////////////////////////////////////////////////////////////////////////////
1145/**
1146 * Creates a route config from a React "children" object, which is usually
1147 * either a `<Route>` element or an array of them. Used internally by
1148 * `<Routes>` to create a route config from its children.
1149 *
1150 * @see https://reactrouter.com/utils/create-routes-from-children
1151 */
1152function createRoutesFromChildren(children, parentPath = []) {
1153 let routes = [];
1154 React.Children.forEach(children, (element, index) => {
1155 if (! /*#__PURE__*/React.isValidElement(element)) {
1156 // Ignore non-elements. This allows people to more easily inline
1157 // conditionals in their route config.
1158 return;
1159 }
1160 let treePath = [...parentPath, index];
1161 if (element.type === React.Fragment) {
1162 // Transparently support React.Fragment and its children.
1163 routes.push.apply(routes, createRoutesFromChildren(element.props.children, treePath));
1164 return;
1165 }
1166 !(element.type === Route) ? UNSAFE_invariant(false, `[${typeof element.type === "string" ? element.type : element.type.name}] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>`) : void 0;
1167 !(!element.props.index || !element.props.children) ? UNSAFE_invariant(false, "An index route cannot have child routes.") : void 0;
1168 let route = {
1169 id: element.props.id || treePath.join("-"),
1170 caseSensitive: element.props.caseSensitive,
1171 element: element.props.element,
1172 Component: element.props.Component,
1173 index: element.props.index,
1174 path: element.props.path,
1175 loader: element.props.loader,
1176 action: element.props.action,
1177 errorElement: element.props.errorElement,
1178 ErrorBoundary: element.props.ErrorBoundary,
1179 hasErrorBoundary: element.props.ErrorBoundary != null || element.props.errorElement != null,
1180 shouldRevalidate: element.props.shouldRevalidate,
1181 handle: element.props.handle,
1182 lazy: element.props.lazy
1183 };
1184 if (element.props.children) {
1185 route.children = createRoutesFromChildren(element.props.children, treePath);
1186 }
1187 routes.push(route);
1188 });
1189 return routes;
1190}
1191/**
1192 * Renders the result of `matchRoutes()` into a React element.
1193 */
1194function renderMatches(matches) {
1195 return _renderMatches(matches);
1196}
1197
1198function mapRouteProperties(route) {
1199 let updates = {
1200 // Note: this check also occurs in createRoutesFromChildren so update
1201 // there if you change this -- please and thank you!
1202 hasErrorBoundary: route.ErrorBoundary != null || route.errorElement != null
1203 };
1204 if (route.Component) {
1205 {
1206 if (route.element) {
1207 UNSAFE_warning(false, "You should not include both `Component` and `element` on your route - " + "`Component` will be used.") ;
1208 }
1209 }
1210 Object.assign(updates, {
1211 element: /*#__PURE__*/React.createElement(route.Component),
1212 Component: undefined
1213 });
1214 }
1215 if (route.ErrorBoundary) {
1216 {
1217 if (route.errorElement) {
1218 UNSAFE_warning(false, "You should not include both `ErrorBoundary` and `errorElement` on your route - " + "`ErrorBoundary` will be used.") ;
1219 }
1220 }
1221 Object.assign(updates, {
1222 errorElement: /*#__PURE__*/React.createElement(route.ErrorBoundary),
1223 ErrorBoundary: undefined
1224 });
1225 }
1226 return updates;
1227}
1228function createMemoryRouter(routes, opts) {
1229 return createRouter({
1230 basename: opts?.basename,
1231 future: {
1232 ...opts?.future,
1233 v7_prependBasename: true
1234 },
1235 history: createMemoryHistory({
1236 initialEntries: opts?.initialEntries,
1237 initialIndex: opts?.initialIndex
1238 }),
1239 hydrationData: opts?.hydrationData,
1240 routes,
1241 mapRouteProperties
1242 }).initialize();
1243}
1244
1245export { Await, MemoryRouter, Navigate, Outlet, Route, Router, RouterProvider, Routes, DataRouterContext as UNSAFE_DataRouterContext, DataRouterStateContext as UNSAFE_DataRouterStateContext, LocationContext as UNSAFE_LocationContext, NavigationContext as UNSAFE_NavigationContext, RouteContext as UNSAFE_RouteContext, mapRouteProperties as UNSAFE_mapRouteProperties, useRouteId as UNSAFE_useRouteId, useRoutesImpl as UNSAFE_useRoutesImpl, createMemoryRouter, createRoutesFromChildren, createRoutesFromChildren as createRoutesFromElements, renderMatches, useBlocker as unstable_useBlocker, useActionData, useAsyncError, useAsyncValue, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes };
1246//# sourceMappingURL=react-router.development.js.map