UNPKG

46.4 kBJavaScriptView Raw
1/**
2 * React Router v6.12.1
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 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 {
593 revalidate: dataRouterContext.router.revalidate,
594 state: state.revalidation
595 };
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(match => {
607 let {
608 pathname,
609 params
610 } = match;
611 // Note: This structure matches that created by createUseMatchesMatch
612 // in the @remix-run/router , so if you change this please also change
613 // that :) Eventually we'll DRY this up
614 return {
615 id: match.route.id,
616 pathname,
617 params,
618 data: loaderData[match.route.id],
619 handle: match.route.handle
620 };
621 }), [matches, loaderData]);
622}
623/**
624 * Returns the loader data for the nearest ancestor Route loader
625 */
626function useLoaderData() {
627 let state = useDataRouterState(DataRouterStateHook.UseLoaderData);
628 let routeId = useCurrentRouteId(DataRouterStateHook.UseLoaderData);
629 if (state.errors && state.errors[routeId] != null) {
630 console.error(`You cannot \`useLoaderData\` in an errorElement (routeId: ${routeId})`);
631 return undefined;
632 }
633 return state.loaderData[routeId];
634}
635/**
636 * Returns the loaderData for the given routeId
637 */
638function useRouteLoaderData(routeId) {
639 let state = useDataRouterState(DataRouterStateHook.UseRouteLoaderData);
640 return state.loaderData[routeId];
641}
642/**
643 * Returns the action data for the nearest ancestor Route action
644 */
645function useActionData() {
646 let state = useDataRouterState(DataRouterStateHook.UseActionData);
647 let route = React.useContext(RouteContext);
648 !route ? UNSAFE_invariant(false, `useActionData must be used inside a RouteContext`) : void 0;
649 return Object.values(state?.actionData || {})[0];
650}
651/**
652 * Returns the nearest ancestor Route error, which could be a loader/action
653 * error or a render error. This is intended to be called from your
654 * ErrorBoundary/errorElement to display a proper error message.
655 */
656function useRouteError() {
657 let error = React.useContext(RouteErrorContext);
658 let state = useDataRouterState(DataRouterStateHook.UseRouteError);
659 let routeId = useCurrentRouteId(DataRouterStateHook.UseRouteError);
660 // If this was a render error, we put it in a RouteError context inside
661 // of RenderErrorBoundary
662 if (error) {
663 return error;
664 }
665 // Otherwise look for errors from our data router state
666 return state.errors?.[routeId];
667}
668/**
669 * Returns the happy-path data from the nearest ancestor <Await /> value
670 */
671function useAsyncValue() {
672 let value = React.useContext(AwaitContext);
673 return value?._data;
674}
675/**
676 * Returns the error from the nearest ancestor <Await /> value
677 */
678function useAsyncError() {
679 let value = React.useContext(AwaitContext);
680 return value?._error;
681}
682let blockerId = 0;
683/**
684 * Allow the application to block navigations within the SPA and present the
685 * user a confirmation dialog to confirm the navigation. Mostly used to avoid
686 * using half-filled form data. This does not handle hard-reloads or
687 * cross-origin navigations.
688 */
689function useBlocker(shouldBlock) {
690 let {
691 router
692 } = useDataRouterContext(DataRouterHook.UseBlocker);
693 let state = useDataRouterState(DataRouterStateHook.UseBlocker);
694 let [blockerKey] = React.useState(() => String(++blockerId));
695 let blockerFunction = React.useCallback(args => {
696 return typeof shouldBlock === "function" ? !!shouldBlock(args) : !!shouldBlock;
697 }, [shouldBlock]);
698 let blocker = router.getBlocker(blockerKey, blockerFunction);
699 // Cleanup on unmount
700 React.useEffect(() => () => router.deleteBlocker(blockerKey), [router, blockerKey]);
701 // Prefer the blocker from state since DataRouterContext is memoized so this
702 // ensures we update on blocker state updates
703 return state.blockers.get(blockerKey) || blocker;
704}
705/**
706 * Stable version of useNavigate that is used when we are in the context of
707 * a RouterProvider.
708 */
709function useNavigateStable() {
710 let {
711 router
712 } = useDataRouterContext(DataRouterHook.UseNavigateStable);
713 let id = useCurrentRouteId(DataRouterStateHook.UseNavigateStable);
714 let activeRef = React.useRef(false);
715 useIsomorphicLayoutEffect(() => {
716 activeRef.current = true;
717 });
718 let navigate = React.useCallback((to, options = {}) => {
719 UNSAFE_warning(activeRef.current, navigateEffectWarning) ;
720 // Short circuit here since if this happens on first render the navigate
721 // is useless because we haven't wired up our router subscriber yet
722 if (!activeRef.current) return;
723 if (typeof to === "number") {
724 router.navigate(to);
725 } else {
726 router.navigate(to, {
727 fromRouteId: id,
728 ...options
729 });
730 }
731 }, [router, id]);
732 return navigate;
733}
734const alreadyWarned = {};
735function warningOnce(key, cond, message) {
736 if (!cond && !alreadyWarned[key]) {
737 alreadyWarned[key] = true;
738 UNSAFE_warning(false, message) ;
739 }
740}
741
742// Webpack + React 17 fails to compile on the usage of `React.startTransition` or
743// `React["startTransition"]` even if it's behind a feature detection of
744// `"startTransition" in React`. Moving this to a constant avoids the issue :/
745const START_TRANSITION = "startTransition";
746/**
747 * Given a Remix Router instance, render the appropriate UI
748 */
749function RouterProvider({
750 fallbackElement,
751 router
752}) {
753 // Need to use a layout effect here so we are subscribed early enough to
754 // pick up on any render-driven redirects/navigations (useEffect/<Navigate>)
755 let [state, setStateImpl] = React.useState(router.state);
756 let setState = React.useCallback(newState => {
757 START_TRANSITION in React ? React[START_TRANSITION](() => setStateImpl(newState)) : setStateImpl(newState);
758 }, [setStateImpl]);
759 React.useLayoutEffect(() => router.subscribe(setState), [router, setState]);
760 let navigator = React.useMemo(() => {
761 return {
762 createHref: router.createHref,
763 encodeLocation: router.encodeLocation,
764 go: n => router.navigate(n),
765 push: (to, state, opts) => router.navigate(to, {
766 state,
767 preventScrollReset: opts?.preventScrollReset
768 }),
769 replace: (to, state, opts) => router.navigate(to, {
770 replace: true,
771 state,
772 preventScrollReset: opts?.preventScrollReset
773 })
774 };
775 }, [router]);
776 let basename = router.basename || "/";
777 let dataRouterContext = React.useMemo(() => ({
778 router,
779 navigator,
780 static: false,
781 basename
782 }), [router, navigator, basename]);
783 // The fragment and {null} here are important! We need them to keep React 18's
784 // useId happy when we are server-rendering since we may have a <script> here
785 // containing the hydrated server-side staticContext (from StaticRouterProvider).
786 // useId relies on the component tree structure to generate deterministic id's
787 // so we need to ensure it remains the same on the client even though
788 // we don't need the <script> tag
789 return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(DataRouterContext.Provider, {
790 value: dataRouterContext
791 }, /*#__PURE__*/React.createElement(DataRouterStateContext.Provider, {
792 value: state
793 }, /*#__PURE__*/React.createElement(Router, {
794 basename: basename,
795 location: state.location,
796 navigationType: state.historyAction,
797 navigator: navigator
798 }, state.initialized ? /*#__PURE__*/React.createElement(DataRoutes, {
799 routes: router.routes,
800 state: state
801 }) : fallbackElement))), null);
802}
803function DataRoutes({
804 routes,
805 state
806}) {
807 return useRoutesImpl(routes, undefined, state);
808}
809/**
810 * A <Router> that stores all entries in memory.
811 *
812 * @see https://reactrouter.com/router-components/memory-router
813 */
814function MemoryRouter({
815 basename,
816 children,
817 initialEntries,
818 initialIndex
819}) {
820 let historyRef = React.useRef();
821 if (historyRef.current == null) {
822 historyRef.current = createMemoryHistory({
823 initialEntries,
824 initialIndex,
825 v5Compat: true
826 });
827 }
828 let history = historyRef.current;
829 let [state, setStateImpl] = React.useState({
830 action: history.action,
831 location: history.location
832 });
833 let setState = React.useCallback(newState => {
834 START_TRANSITION in React ? React[START_TRANSITION](() => setStateImpl(newState)) : setStateImpl(newState);
835 }, [setStateImpl]);
836 React.useLayoutEffect(() => history.listen(setState), [history, setState]);
837 return /*#__PURE__*/React.createElement(Router, {
838 basename: basename,
839 children: children,
840 location: state.location,
841 navigationType: state.action,
842 navigator: history
843 });
844}
845/**
846 * Changes the current location.
847 *
848 * Note: This API is mostly useful in React.Component subclasses that are not
849 * able to use hooks. In functional components, we recommend you use the
850 * `useNavigate` hook instead.
851 *
852 * @see https://reactrouter.com/components/navigate
853 */
854function Navigate({
855 to,
856 replace,
857 state,
858 relative
859}) {
860 !useInRouterContext() ? UNSAFE_invariant(false,
861 // TODO: This error is probably because they somehow have 2 versions of
862 // the router loaded. We can help them understand how to avoid that.
863 `<Navigate> may be used only in the context of a <Router> component.`) : void 0;
864 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.`) ;
865 let {
866 matches
867 } = React.useContext(RouteContext);
868 let {
869 pathname: locationPathname
870 } = useLocation();
871 let navigate = useNavigate();
872 // Resolve the path outside of the effect so that when effects run twice in
873 // StrictMode they navigate to the same place
874 let path = resolveTo(to, UNSAFE_getPathContributingMatches(matches).map(match => match.pathnameBase), locationPathname, relative === "path");
875 let jsonPath = JSON.stringify(path);
876 React.useEffect(() => navigate(JSON.parse(jsonPath), {
877 replace,
878 state,
879 relative
880 }), [navigate, jsonPath, relative, replace, state]);
881 return null;
882}
883/**
884 * Renders the child route's element, if there is one.
885 *
886 * @see https://reactrouter.com/components/outlet
887 */
888function Outlet(props) {
889 return useOutlet(props.context);
890}
891/**
892 * Declares an element that should be rendered at a certain URL path.
893 *
894 * @see https://reactrouter.com/components/route
895 */
896function Route(_props) {
897 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>.`) ;
898}
899/**
900 * Provides location context for the rest of the app.
901 *
902 * Note: You usually won't render a <Router> directly. Instead, you'll render a
903 * router that is more specific to your environment such as a <BrowserRouter>
904 * in web browsers or a <StaticRouter> for server rendering.
905 *
906 * @see https://reactrouter.com/router-components/router
907 */
908function Router({
909 basename: basenameProp = "/",
910 children = null,
911 location: locationProp,
912 navigationType = Action.Pop,
913 navigator,
914 static: staticProp = false
915}) {
916 !!useInRouterContext() ? UNSAFE_invariant(false, `You cannot render a <Router> inside another <Router>.` + ` You should never have more than one in your app.`) : void 0;
917 // Preserve trailing slashes on basename, so we can let the user control
918 // the enforcement of trailing slashes throughout the app
919 let basename = basenameProp.replace(/^\/*/, "/");
920 let navigationContext = React.useMemo(() => ({
921 basename,
922 navigator,
923 static: staticProp
924 }), [basename, navigator, staticProp]);
925 if (typeof locationProp === "string") {
926 locationProp = parsePath(locationProp);
927 }
928 let {
929 pathname = "/",
930 search = "",
931 hash = "",
932 state = null,
933 key = "default"
934 } = locationProp;
935 let locationContext = React.useMemo(() => {
936 let trailingPathname = stripBasename(pathname, basename);
937 if (trailingPathname == null) {
938 return null;
939 }
940 return {
941 location: {
942 pathname: trailingPathname,
943 search,
944 hash,
945 state,
946 key
947 },
948 navigationType
949 };
950 }, [basename, pathname, search, hash, state, key, navigationType]);
951 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.`) ;
952 if (locationContext == null) {
953 return null;
954 }
955 return /*#__PURE__*/React.createElement(NavigationContext.Provider, {
956 value: navigationContext
957 }, /*#__PURE__*/React.createElement(LocationContext.Provider, {
958 children: children,
959 value: locationContext
960 }));
961}
962/**
963 * A container for a nested tree of <Route> elements that renders the branch
964 * that best matches the current location.
965 *
966 * @see https://reactrouter.com/components/routes
967 */
968function Routes({
969 children,
970 location
971}) {
972 return useRoutes(createRoutesFromChildren(children), location);
973}
974/**
975 * Component to use for rendering lazily loaded data from returning defer()
976 * in a loader function
977 */
978function Await({
979 children,
980 errorElement,
981 resolve
982}) {
983 return /*#__PURE__*/React.createElement(AwaitErrorBoundary, {
984 resolve: resolve,
985 errorElement: errorElement
986 }, /*#__PURE__*/React.createElement(ResolveAwait, null, children));
987}
988var AwaitRenderStatus;
989(function (AwaitRenderStatus) {
990 AwaitRenderStatus[AwaitRenderStatus["pending"] = 0] = "pending";
991 AwaitRenderStatus[AwaitRenderStatus["success"] = 1] = "success";
992 AwaitRenderStatus[AwaitRenderStatus["error"] = 2] = "error";
993})(AwaitRenderStatus || (AwaitRenderStatus = {}));
994const neverSettledPromise = new Promise(() => {});
995class AwaitErrorBoundary extends React.Component {
996 constructor(props) {
997 super(props);
998 this.state = {
999 error: null
1000 };
1001 }
1002 static getDerivedStateFromError(error) {
1003 return {
1004 error
1005 };
1006 }
1007 componentDidCatch(error, errorInfo) {
1008 console.error("<Await> caught the following error during render", error, errorInfo);
1009 }
1010 render() {
1011 let {
1012 children,
1013 errorElement,
1014 resolve
1015 } = this.props;
1016 let promise = null;
1017 let status = AwaitRenderStatus.pending;
1018 if (!(resolve instanceof Promise)) {
1019 // Didn't get a promise - provide as a resolved promise
1020 status = AwaitRenderStatus.success;
1021 promise = Promise.resolve();
1022 Object.defineProperty(promise, "_tracked", {
1023 get: () => true
1024 });
1025 Object.defineProperty(promise, "_data", {
1026 get: () => resolve
1027 });
1028 } else if (this.state.error) {
1029 // Caught a render error, provide it as a rejected promise
1030 status = AwaitRenderStatus.error;
1031 let renderError = this.state.error;
1032 promise = Promise.reject().catch(() => {}); // Avoid unhandled rejection warnings
1033 Object.defineProperty(promise, "_tracked", {
1034 get: () => true
1035 });
1036 Object.defineProperty(promise, "_error", {
1037 get: () => renderError
1038 });
1039 } else if (resolve._tracked) {
1040 // Already tracked promise - check contents
1041 promise = resolve;
1042 status = promise._error !== undefined ? AwaitRenderStatus.error : promise._data !== undefined ? AwaitRenderStatus.success : AwaitRenderStatus.pending;
1043 } else {
1044 // Raw (untracked) promise - track it
1045 status = AwaitRenderStatus.pending;
1046 Object.defineProperty(resolve, "_tracked", {
1047 get: () => true
1048 });
1049 promise = resolve.then(data => Object.defineProperty(resolve, "_data", {
1050 get: () => data
1051 }), error => Object.defineProperty(resolve, "_error", {
1052 get: () => error
1053 }));
1054 }
1055 if (status === AwaitRenderStatus.error && promise._error instanceof AbortedDeferredError) {
1056 // Freeze the UI by throwing a never resolved promise
1057 throw neverSettledPromise;
1058 }
1059 if (status === AwaitRenderStatus.error && !errorElement) {
1060 // No errorElement, throw to the nearest route-level error boundary
1061 throw promise._error;
1062 }
1063 if (status === AwaitRenderStatus.error) {
1064 // Render via our errorElement
1065 return /*#__PURE__*/React.createElement(AwaitContext.Provider, {
1066 value: promise,
1067 children: errorElement
1068 });
1069 }
1070 if (status === AwaitRenderStatus.success) {
1071 // Render children with resolved value
1072 return /*#__PURE__*/React.createElement(AwaitContext.Provider, {
1073 value: promise,
1074 children: children
1075 });
1076 }
1077 // Throw to the suspense boundary
1078 throw promise;
1079 }
1080}
1081/**
1082 * @private
1083 * Indirection to leverage useAsyncValue for a render-prop API on <Await>
1084 */
1085function ResolveAwait({
1086 children
1087}) {
1088 let data = useAsyncValue();
1089 let toRender = typeof children === "function" ? children(data) : children;
1090 return /*#__PURE__*/React.createElement(React.Fragment, null, toRender);
1091}
1092///////////////////////////////////////////////////////////////////////////////
1093// UTILS
1094///////////////////////////////////////////////////////////////////////////////
1095/**
1096 * Creates a route config from a React "children" object, which is usually
1097 * either a `<Route>` element or an array of them. Used internally by
1098 * `<Routes>` to create a route config from its children.
1099 *
1100 * @see https://reactrouter.com/utils/create-routes-from-children
1101 */
1102function createRoutesFromChildren(children, parentPath = []) {
1103 let routes = [];
1104 React.Children.forEach(children, (element, index) => {
1105 if (! /*#__PURE__*/React.isValidElement(element)) {
1106 // Ignore non-elements. This allows people to more easily inline
1107 // conditionals in their route config.
1108 return;
1109 }
1110 let treePath = [...parentPath, index];
1111 if (element.type === React.Fragment) {
1112 // Transparently support React.Fragment and its children.
1113 routes.push.apply(routes, createRoutesFromChildren(element.props.children, treePath));
1114 return;
1115 }
1116 !(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;
1117 !(!element.props.index || !element.props.children) ? UNSAFE_invariant(false, "An index route cannot have child routes.") : void 0;
1118 let route = {
1119 id: element.props.id || treePath.join("-"),
1120 caseSensitive: element.props.caseSensitive,
1121 element: element.props.element,
1122 Component: element.props.Component,
1123 index: element.props.index,
1124 path: element.props.path,
1125 loader: element.props.loader,
1126 action: element.props.action,
1127 errorElement: element.props.errorElement,
1128 ErrorBoundary: element.props.ErrorBoundary,
1129 hasErrorBoundary: element.props.ErrorBoundary != null || element.props.errorElement != null,
1130 shouldRevalidate: element.props.shouldRevalidate,
1131 handle: element.props.handle,
1132 lazy: element.props.lazy
1133 };
1134 if (element.props.children) {
1135 route.children = createRoutesFromChildren(element.props.children, treePath);
1136 }
1137 routes.push(route);
1138 });
1139 return routes;
1140}
1141/**
1142 * Renders the result of `matchRoutes()` into a React element.
1143 */
1144function renderMatches(matches) {
1145 return _renderMatches(matches);
1146}
1147
1148function mapRouteProperties(route) {
1149 let updates = {
1150 // Note: this check also occurs in createRoutesFromChildren so update
1151 // there if you change this -- please and thank you!
1152 hasErrorBoundary: route.ErrorBoundary != null || route.errorElement != null
1153 };
1154 if (route.Component) {
1155 {
1156 if (route.element) {
1157 UNSAFE_warning(false, "You should not include both `Component` and `element` on your route - " + "`Component` will be used.") ;
1158 }
1159 }
1160 Object.assign(updates, {
1161 element: /*#__PURE__*/React.createElement(route.Component),
1162 Component: undefined
1163 });
1164 }
1165 if (route.ErrorBoundary) {
1166 {
1167 if (route.errorElement) {
1168 UNSAFE_warning(false, "You should not include both `ErrorBoundary` and `errorElement` on your route - " + "`ErrorBoundary` will be used.") ;
1169 }
1170 }
1171 Object.assign(updates, {
1172 errorElement: /*#__PURE__*/React.createElement(route.ErrorBoundary),
1173 ErrorBoundary: undefined
1174 });
1175 }
1176 return updates;
1177}
1178function createMemoryRouter(routes, opts) {
1179 return createRouter({
1180 basename: opts?.basename,
1181 future: {
1182 ...opts?.future,
1183 v7_prependBasename: true
1184 },
1185 history: createMemoryHistory({
1186 initialEntries: opts?.initialEntries,
1187 initialIndex: opts?.initialIndex
1188 }),
1189 hydrationData: opts?.hydrationData,
1190 routes,
1191 mapRouteProperties
1192 }).initialize();
1193}
1194
1195export { 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 };
1196//# sourceMappingURL=react-router.development.js.map