UNPKG

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