/** * React Router v6.16.0 * * Copyright (c) Remix Software Inc. * * This source code is licensed under the MIT license found in the * LICENSE.md file in the root directory of this source tree. * * @license MIT */ import * as React from 'react'; import { UNSAFE_invariant, joinPaths, matchPath, UNSAFE_getPathContributingMatches, UNSAFE_warning, resolveTo, parsePath, matchRoutes, Action, UNSAFE_convertRouteMatchToUiMatch, stripBasename, IDLE_BLOCKER, isRouteErrorResponse, createMemoryHistory, AbortedDeferredError, createRouter } from '@remix-run/router'; export { AbortedDeferredError, Action as NavigationType, createPath, defer, generatePath, isRouteErrorResponse, json, matchPath, matchRoutes, parsePath, redirect, redirectDocument, resolvePath } from '@remix-run/router'; const DataRouterContext = /*#__PURE__*/React.createContext(null); { DataRouterContext.displayName = "DataRouter"; } const DataRouterStateContext = /*#__PURE__*/React.createContext(null); { DataRouterStateContext.displayName = "DataRouterState"; } const AwaitContext = /*#__PURE__*/React.createContext(null); { AwaitContext.displayName = "Await"; } const NavigationContext = /*#__PURE__*/React.createContext(null); { NavigationContext.displayName = "Navigation"; } const LocationContext = /*#__PURE__*/React.createContext(null); { LocationContext.displayName = "Location"; } const RouteContext = /*#__PURE__*/React.createContext({ outlet: null, matches: [], isDataRoute: false }); { RouteContext.displayName = "Route"; } const RouteErrorContext = /*#__PURE__*/React.createContext(null); { RouteErrorContext.displayName = "RouteError"; } /** * Returns the full href for the given "to" value. This is useful for building * custom links that are also accessible and preserve right-click behavior. * * @see https://reactrouter.com/hooks/use-href */ function useHref(to, { relative } = {}) { !useInRouterContext() ? UNSAFE_invariant(false, // TODO: This error is probably because they somehow have 2 versions of the // router loaded. We can help them understand how to avoid that. `useHref() may be used only in the context of a component.`) : void 0; let { basename, navigator } = React.useContext(NavigationContext); let { hash, pathname, search } = useResolvedPath(to, { relative }); let joinedPathname = pathname; // If we're operating within a basename, prepend it to the pathname prior // to creating the href. If this is a root navigation, then just use the raw // basename which allows the basename to have full control over the presence // of a trailing slash on root links if (basename !== "/") { joinedPathname = pathname === "/" ? basename : joinPaths([basename, pathname]); } return navigator.createHref({ pathname: joinedPathname, search, hash }); } /** * Returns true if this component is a descendant of a . * * @see https://reactrouter.com/hooks/use-in-router-context */ function useInRouterContext() { return React.useContext(LocationContext) != null; } /** * Returns the current location object, which represents the current URL in web * browsers. * * Note: If you're using this it may mean you're doing some of your own * "routing" in your app, and we'd like to know what your use case is. We may * be able to provide something higher-level to better suit your needs. * * @see https://reactrouter.com/hooks/use-location */ function useLocation() { !useInRouterContext() ? UNSAFE_invariant(false, // TODO: This error is probably because they somehow have 2 versions of the // router loaded. We can help them understand how to avoid that. `useLocation() may be used only in the context of a component.`) : void 0; return React.useContext(LocationContext).location; } /** * Returns the current navigation action which describes how the router came to * the current location, either by a pop, push, or replace on the history stack. * * @see https://reactrouter.com/hooks/use-navigation-type */ function useNavigationType() { return React.useContext(LocationContext).navigationType; } /** * Returns a PathMatch object if the given pattern matches the current URL. * This is useful for components that need to know "active" state, e.g. * . * * @see https://reactrouter.com/hooks/use-match */ function useMatch(pattern) { !useInRouterContext() ? UNSAFE_invariant(false, // TODO: This error is probably because they somehow have 2 versions of the // router loaded. We can help them understand how to avoid that. `useMatch() may be used only in the context of a component.`) : void 0; let { pathname } = useLocation(); return React.useMemo(() => matchPath(pattern, pathname), [pathname, pattern]); } const 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 function useIsomorphicLayoutEffect(cb) { let isStatic = React.useContext(NavigationContext).static; if (!isStatic) { // We should be able to get rid of this once react 18.3 is released // See: https://github.com/facebook/react/pull/26395 // eslint-disable-next-line react-hooks/rules-of-hooks React.useLayoutEffect(cb); } } /** * Returns an imperative method for changing the location. Used by s, but * may also be used by other elements to change the location. * * @see https://reactrouter.com/hooks/use-navigate */ function useNavigate() { let { isDataRoute } = React.useContext(RouteContext); // Conditional usage is OK here because the usage of a data router is static // eslint-disable-next-line react-hooks/rules-of-hooks return isDataRoute ? useNavigateStable() : useNavigateUnstable(); } function useNavigateUnstable() { !useInRouterContext() ? UNSAFE_invariant(false, // TODO: This error is probably because they somehow have 2 versions of the // router loaded. We can help them understand how to avoid that. `useNavigate() may be used only in the context of a component.`) : void 0; let dataRouterContext = React.useContext(DataRouterContext); let { basename, navigator } = React.useContext(NavigationContext); let { matches } = React.useContext(RouteContext); let { pathname: locationPathname } = useLocation(); let routePathnamesJson = JSON.stringify(UNSAFE_getPathContributingMatches(matches).map(match => match.pathnameBase)); let activeRef = React.useRef(false); useIsomorphicLayoutEffect(() => { activeRef.current = true; }); let navigate = React.useCallback((to, options = {}) => { UNSAFE_warning(activeRef.current, navigateEffectWarning) ; // Short circuit here since if this happens on first render the navigate // is useless because we haven't wired up our history listener yet if (!activeRef.current) return; if (typeof to === "number") { navigator.go(to); return; } let path = resolveTo(to, JSON.parse(routePathnamesJson), locationPathname, options.relative === "path"); // If we're operating within a basename, prepend it to the pathname prior // to handing off to history (but only if we're not in a data router, // otherwise it'll prepend the basename inside of the router). // If this is a root navigation, then we navigate to the raw basename // which allows the basename to have full control over the presence of a // trailing slash on root links if (dataRouterContext == null && basename !== "/") { path.pathname = path.pathname === "/" ? basename : joinPaths([basename, path.pathname]); } (!!options.replace ? navigator.replace : navigator.push)(path, options.state, options); }, [basename, navigator, routePathnamesJson, locationPathname, dataRouterContext]); return navigate; } const OutletContext = /*#__PURE__*/React.createContext(null); /** * Returns the context (if provided) for the child route at this level of the route * hierarchy. * @see https://reactrouter.com/hooks/use-outlet-context */ function useOutletContext() { return React.useContext(OutletContext); } /** * Returns the element for the child route at this level of the route * hierarchy. Used internally by to render child routes. * * @see https://reactrouter.com/hooks/use-outlet */ function useOutlet(context) { let outlet = React.useContext(RouteContext).outlet; if (outlet) { return /*#__PURE__*/React.createElement(OutletContext.Provider, { value: context }, outlet); } return outlet; } /** * Returns an object of key/value pairs of the dynamic params from the current * URL that were matched by the route path. * * @see https://reactrouter.com/hooks/use-params */ function useParams() { let { matches } = React.useContext(RouteContext); let routeMatch = matches[matches.length - 1]; return routeMatch ? routeMatch.params : {}; } /** * Resolves the pathname of the given `to` value against the current location. * * @see https://reactrouter.com/hooks/use-resolved-path */ function useResolvedPath(to, { relative } = {}) { let { matches } = React.useContext(RouteContext); let { pathname: locationPathname } = useLocation(); let routePathnamesJson = JSON.stringify(UNSAFE_getPathContributingMatches(matches).map(match => match.pathnameBase)); return React.useMemo(() => resolveTo(to, JSON.parse(routePathnamesJson), locationPathname, relative === "path"), [to, routePathnamesJson, locationPathname, relative]); } /** * Returns the element of the route that matched the current location, prepared * with the correct context to render the remainder of the route tree. Route * elements in the tree must render an to render their child route's * element. * * @see https://reactrouter.com/hooks/use-routes */ function useRoutes(routes, locationArg) { return useRoutesImpl(routes, locationArg); } // Internal implementation with accept optional param for RouterProvider usage function useRoutesImpl(routes, locationArg, dataRouterState) { !useInRouterContext() ? UNSAFE_invariant(false, // TODO: This error is probably because they somehow have 2 versions of the // router loaded. We can help them understand how to avoid that. `useRoutes() may be used only in the context of a component.`) : void 0; let { navigator } = React.useContext(NavigationContext); let { matches: parentMatches } = React.useContext(RouteContext); let routeMatch = parentMatches[parentMatches.length - 1]; let parentParams = routeMatch ? routeMatch.params : {}; let parentPathname = routeMatch ? routeMatch.pathname : "/"; let parentPathnameBase = routeMatch ? routeMatch.pathnameBase : "/"; let parentRoute = routeMatch && routeMatch.route; { // You won't get a warning about 2 different under a // without a trailing *, but this is a best-effort warning anyway since we // cannot even give the warning unless they land at the parent route. // // Example: // // // {/* This route path MUST end with /* because otherwise // it will never match /blog/post/123 */} // } /> // } /> // // // function Blog() { // return ( // // } /> // // ); // } let parentPath = parentRoute && parentRoute.path || ""; warningOnce(parentPathname, !parentRoute || parentPath.endsWith("*"), `You rendered descendant (or called \`useRoutes()\`) at ` + `"${parentPathname}" (under ) 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 to .`); } let locationFromContext = useLocation(); let location; if (locationArg) { let parsedLocationArg = typeof locationArg === "string" ? parsePath(locationArg) : locationArg; !(parentPathnameBase === "/" || parsedLocationArg.pathname?.startsWith(parentPathnameBase)) ? UNSAFE_invariant(false, `When overriding the location using \`\` 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; location = parsedLocationArg; } else { location = locationFromContext; } let pathname = location.pathname || "/"; let remainingPathname = parentPathnameBase === "/" ? pathname : pathname.slice(parentPathnameBase.length) || "/"; let matches = matchRoutes(routes, { pathname: remainingPathname }); { UNSAFE_warning(parentRoute || matches != null, `No routes matched location "${location.pathname}${location.search}${location.hash}" `) ; 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 with a ` + `null value by default resulting in an "empty" page.`) ; } let renderedMatches = _renderMatches(matches && matches.map(match => Object.assign({}, match, { params: Object.assign({}, parentParams, match.params), pathname: joinPaths([parentPathnameBase, // Re-encode pathnames that were decoded inside matchRoutes navigator.encodeLocation ? navigator.encodeLocation(match.pathname).pathname : match.pathname]), pathnameBase: match.pathnameBase === "/" ? parentPathnameBase : joinPaths([parentPathnameBase, // Re-encode pathnames that were decoded inside matchRoutes navigator.encodeLocation ? navigator.encodeLocation(match.pathnameBase).pathname : match.pathnameBase]) })), parentMatches, dataRouterState); // When a user passes in a `locationArg`, the associated routes need to // be wrapped in a new `LocationContext.Provider` in order for `useLocation` // to use the scoped location instead of the global location. if (locationArg && renderedMatches) { return /*#__PURE__*/React.createElement(LocationContext.Provider, { value: { location: { pathname: "/", search: "", hash: "", state: null, key: "default", ...location }, navigationType: Action.Pop } }, renderedMatches); } return renderedMatches; } function DefaultErrorComponent() { let error = useRouteError(); let message = isRouteErrorResponse(error) ? `${error.status} ${error.statusText}` : error instanceof Error ? error.message : JSON.stringify(error); let stack = error instanceof Error ? error.stack : null; let lightgrey = "rgba(200,200,200, 0.5)"; let preStyles = { padding: "0.5rem", backgroundColor: lightgrey }; let codeStyles = { padding: "2px 4px", backgroundColor: lightgrey }; let devInfo = null; { console.error("Error handled by React Router default ErrorBoundary:", error); 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", { style: codeStyles }, "ErrorBoundary"), " or", " ", /*#__PURE__*/React.createElement("code", { style: codeStyles }, "errorElement"), " prop on your route.")); } return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("h2", null, "Unexpected Application Error!"), /*#__PURE__*/React.createElement("h3", { style: { fontStyle: "italic" } }, message), stack ? /*#__PURE__*/React.createElement("pre", { style: preStyles }, stack) : null, devInfo); } const defaultErrorElement = /*#__PURE__*/React.createElement(DefaultErrorComponent, null); class RenderErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { location: props.location, revalidation: props.revalidation, error: props.error }; } static getDerivedStateFromError(error) { return { error: error }; } static getDerivedStateFromProps(props, state) { // When we get into an error state, the user will likely click "back" to the // previous page that didn't have an error. Because this wraps the entire // application, that will have no effect--the error page continues to display. // This gives us a mechanism to recover from the error when the location changes. // // Whether we're in an error state or not, we update the location in state // so that when we are in an error state, it gets reset when a new location // comes in and the user recovers from the error. if (state.location !== props.location || state.revalidation !== "idle" && props.revalidation === "idle") { return { error: props.error, location: props.location, revalidation: props.revalidation }; } // If we're not changing locations, preserve the location but still surface // any new errors that may come through. We retain the existing error, we do // this because the error provided from the app state may be cleared without // the location changing. return { error: props.error || state.error, location: state.location, revalidation: props.revalidation || state.revalidation }; } componentDidCatch(error, errorInfo) { console.error("React Router caught the following error during render", error, errorInfo); } render() { return this.state.error ? /*#__PURE__*/React.createElement(RouteContext.Provider, { value: this.props.routeContext }, /*#__PURE__*/React.createElement(RouteErrorContext.Provider, { value: this.state.error, children: this.props.component })) : this.props.children; } } function RenderedRoute({ routeContext, match, children }) { let dataRouterContext = React.useContext(DataRouterContext); // Track how deep we got in our render pass to emulate SSR componentDidCatch // in a DataStaticRouter if (dataRouterContext && dataRouterContext.static && dataRouterContext.staticContext && (match.route.errorElement || match.route.ErrorBoundary)) { dataRouterContext.staticContext._deepestRenderedBoundaryId = match.route.id; } return /*#__PURE__*/React.createElement(RouteContext.Provider, { value: routeContext }, children); } function _renderMatches(matches, parentMatches = [], dataRouterState = null) { if (matches == null) { if (dataRouterState?.errors) { // Don't bail if we have data router errors so we can render them in the // boundary. Use the pre-matched (or shimmed) matches matches = dataRouterState.matches; } else { return null; } } let renderedMatches = matches; // If we have data errors, trim matches to the highest error boundary let errors = dataRouterState?.errors; if (errors != null) { let errorIndex = renderedMatches.findIndex(m => m.route.id && errors?.[m.route.id]); !(errorIndex >= 0) ? UNSAFE_invariant(false, `Could not find a matching route for errors on route IDs: ${Object.keys(errors).join(",")}`) : void 0; renderedMatches = renderedMatches.slice(0, Math.min(renderedMatches.length, errorIndex + 1)); } return renderedMatches.reduceRight((outlet, match, index) => { let error = match.route.id ? errors?.[match.route.id] : null; // Only data routers handle errors let errorElement = null; if (dataRouterState) { errorElement = match.route.errorElement || defaultErrorElement; } let matches = parentMatches.concat(renderedMatches.slice(0, index + 1)); let getChildren = () => { let children; if (error) { children = errorElement; } else if (match.route.Component) { // Note: This is a de-optimized path since React won't re-use the // ReactElement since it's identity changes with each new // React.createElement call. We keep this so folks can use // `` in `` but generally `Component` // usage is only advised in `RouterProvider` when we can convert it to // `element` ahead of time. children = /*#__PURE__*/React.createElement(match.route.Component, null); } else if (match.route.element) { children = match.route.element; } else { children = outlet; } return /*#__PURE__*/React.createElement(RenderedRoute, { match: match, routeContext: { outlet, matches, isDataRoute: dataRouterState != null }, children: children }); }; // Only wrap in an error boundary within data router usages when we have an // ErrorBoundary/errorElement on this route. Otherwise let it bubble up to // an ancestor ErrorBoundary/errorElement return dataRouterState && (match.route.ErrorBoundary || match.route.errorElement || index === 0) ? /*#__PURE__*/React.createElement(RenderErrorBoundary, { location: dataRouterState.location, revalidation: dataRouterState.revalidation, component: errorElement, error: error, children: getChildren(), routeContext: { outlet: null, matches, isDataRoute: true } }) : getChildren(); }, null); } var DataRouterHook; (function (DataRouterHook) { DataRouterHook["UseBlocker"] = "useBlocker"; DataRouterHook["UseRevalidator"] = "useRevalidator"; DataRouterHook["UseNavigateStable"] = "useNavigate"; })(DataRouterHook || (DataRouterHook = {})); var DataRouterStateHook; (function (DataRouterStateHook) { DataRouterStateHook["UseBlocker"] = "useBlocker"; DataRouterStateHook["UseLoaderData"] = "useLoaderData"; DataRouterStateHook["UseActionData"] = "useActionData"; DataRouterStateHook["UseRouteError"] = "useRouteError"; DataRouterStateHook["UseNavigation"] = "useNavigation"; DataRouterStateHook["UseRouteLoaderData"] = "useRouteLoaderData"; DataRouterStateHook["UseMatches"] = "useMatches"; DataRouterStateHook["UseRevalidator"] = "useRevalidator"; DataRouterStateHook["UseNavigateStable"] = "useNavigate"; DataRouterStateHook["UseRouteId"] = "useRouteId"; })(DataRouterStateHook || (DataRouterStateHook = {})); function getDataRouterConsoleError(hookName) { return `${hookName} must be used within a data router. See https://reactrouter.com/routers/picking-a-router.`; } function useDataRouterContext(hookName) { let ctx = React.useContext(DataRouterContext); !ctx ? UNSAFE_invariant(false, getDataRouterConsoleError(hookName)) : void 0; return ctx; } function useDataRouterState(hookName) { let state = React.useContext(DataRouterStateContext); !state ? UNSAFE_invariant(false, getDataRouterConsoleError(hookName)) : void 0; return state; } function useRouteContext(hookName) { let route = React.useContext(RouteContext); !route ? UNSAFE_invariant(false, getDataRouterConsoleError(hookName)) : void 0; return route; } // Internal version with hookName-aware debugging function useCurrentRouteId(hookName) { let route = useRouteContext(hookName); let thisRoute = route.matches[route.matches.length - 1]; !thisRoute.route.id ? UNSAFE_invariant(false, `${hookName} can only be used on routes that contain a unique "id"`) : void 0; return thisRoute.route.id; } /** * Returns the ID for the nearest contextual route */ function useRouteId() { return useCurrentRouteId(DataRouterStateHook.UseRouteId); } /** * Returns the current navigation, defaulting to an "idle" navigation when * no navigation is in progress */ function useNavigation() { let state = useDataRouterState(DataRouterStateHook.UseNavigation); return state.navigation; } /** * Returns a revalidate function for manually triggering revalidation, as well * as the current state of any manual revalidations */ function useRevalidator() { let dataRouterContext = useDataRouterContext(DataRouterHook.UseRevalidator); let state = useDataRouterState(DataRouterStateHook.UseRevalidator); return React.useMemo(() => ({ revalidate: dataRouterContext.router.revalidate, state: state.revalidation }), [dataRouterContext.router.revalidate, state.revalidation]); } /** * Returns the active route matches, useful for accessing loaderData for * parent/child routes or the route "handle" property */ function useMatches() { let { matches, loaderData } = useDataRouterState(DataRouterStateHook.UseMatches); return React.useMemo(() => matches.map(m => UNSAFE_convertRouteMatchToUiMatch(m, loaderData)), [matches, loaderData]); } /** * Returns the loader data for the nearest ancestor Route loader */ function useLoaderData() { let state = useDataRouterState(DataRouterStateHook.UseLoaderData); let routeId = useCurrentRouteId(DataRouterStateHook.UseLoaderData); if (state.errors && state.errors[routeId] != null) { console.error(`You cannot \`useLoaderData\` in an errorElement (routeId: ${routeId})`); return undefined; } return state.loaderData[routeId]; } /** * Returns the loaderData for the given routeId */ function useRouteLoaderData(routeId) { let state = useDataRouterState(DataRouterStateHook.UseRouteLoaderData); return state.loaderData[routeId]; } /** * Returns the action data for the nearest ancestor Route action */ function useActionData() { let state = useDataRouterState(DataRouterStateHook.UseActionData); let route = React.useContext(RouteContext); !route ? UNSAFE_invariant(false, `useActionData must be used inside a RouteContext`) : void 0; return Object.values(state?.actionData || {})[0]; } /** * Returns the nearest ancestor Route error, which could be a loader/action * error or a render error. This is intended to be called from your * ErrorBoundary/errorElement to display a proper error message. */ function useRouteError() { let error = React.useContext(RouteErrorContext); let state = useDataRouterState(DataRouterStateHook.UseRouteError); let routeId = useCurrentRouteId(DataRouterStateHook.UseRouteError); // If this was a render error, we put it in a RouteError context inside // of RenderErrorBoundary if (error) { return error; } // Otherwise look for errors from our data router state return state.errors?.[routeId]; } /** * Returns the happy-path data from the nearest ancestor value */ function useAsyncValue() { let value = React.useContext(AwaitContext); return value?._data; } /** * Returns the error from the nearest ancestor value */ function useAsyncError() { let value = React.useContext(AwaitContext); return value?._error; } let blockerId = 0; /** * Allow the application to block navigations within the SPA and present the * user a confirmation dialog to confirm the navigation. Mostly used to avoid * using half-filled form data. This does not handle hard-reloads or * cross-origin navigations. */ function useBlocker(shouldBlock) { let { router, basename } = useDataRouterContext(DataRouterHook.UseBlocker); let state = useDataRouterState(DataRouterStateHook.UseBlocker); let [blockerKey, setBlockerKey] = React.useState(""); let blockerFunction = React.useCallback(arg => { if (typeof shouldBlock !== "function") { return !!shouldBlock; } if (basename === "/") { return shouldBlock(arg); } // If they provided us a function and we've got an active basename, strip // it from the locations we expose to the user to match the behavior of // useLocation let { currentLocation, nextLocation, historyAction } = arg; return shouldBlock({ currentLocation: { ...currentLocation, pathname: stripBasename(currentLocation.pathname, basename) || currentLocation.pathname }, nextLocation: { ...nextLocation, pathname: stripBasename(nextLocation.pathname, basename) || nextLocation.pathname }, historyAction }); }, [basename, shouldBlock]); // This effect is in charge of blocker key assignment and deletion (which is // tightly coupled to the key) React.useEffect(() => { let key = String(++blockerId); setBlockerKey(key); return () => router.deleteBlocker(key); }, [router]); // This effect handles assigning the blockerFunction. This is to handle // unstable blocker function identities, and happens only after the prior // effect so we don't get an orphaned blockerFunction in the router with a // key of "". Until then we just have the IDLE_BLOCKER. React.useEffect(() => { if (blockerKey !== "") { router.getBlocker(blockerKey, blockerFunction); } }, [router, blockerKey, blockerFunction]); // Prefer the blocker from `state` not `router.state` since DataRouterContext // is memoized so this ensures we update on blocker state updates return blockerKey && state.blockers.has(blockerKey) ? state.blockers.get(blockerKey) : IDLE_BLOCKER; } /** * Stable version of useNavigate that is used when we are in the context of * a RouterProvider. */ function useNavigateStable() { let { router } = useDataRouterContext(DataRouterHook.UseNavigateStable); let id = useCurrentRouteId(DataRouterStateHook.UseNavigateStable); let activeRef = React.useRef(false); useIsomorphicLayoutEffect(() => { activeRef.current = true; }); let navigate = React.useCallback((to, options = {}) => { UNSAFE_warning(activeRef.current, navigateEffectWarning) ; // Short circuit here since if this happens on first render the navigate // is useless because we haven't wired up our router subscriber yet if (!activeRef.current) return; if (typeof to === "number") { router.navigate(to); } else { router.navigate(to, { fromRouteId: id, ...options }); } }, [router, id]); return navigate; } const alreadyWarned = {}; function warningOnce(key, cond, message) { if (!cond && !alreadyWarned[key]) { alreadyWarned[key] = true; UNSAFE_warning(false, message) ; } } /** Webpack + React 17 fails to compile on any of the following because webpack complains that `startTransition` doesn't exist in `React`: * import { startTransition } from "react" * import * as React from from "react"; "startTransition" in React ? React.startTransition(() => setState()) : setState() * import * as React from from "react"; "startTransition" in React ? React["startTransition"](() => setState()) : setState() Moving it to a constant such as the following solves the Webpack/React 17 issue: * import * as React from from "react"; const START_TRANSITION = "startTransition"; START_TRANSITION in React ? React[START_TRANSITION](() => setState()) : setState() However, that introduces webpack/terser minification issues in production builds in React 18 where minification/obfuscation ends up removing the call of React.startTransition entirely from the first half of the ternary. Grabbing this exported reference once up front resolves that issue. See https://github.com/remix-run/react-router/issues/10579 */ const START_TRANSITION = "startTransition"; const startTransitionImpl = React[START_TRANSITION]; /** * Given a Remix Router instance, render the appropriate UI */ function RouterProvider({ fallbackElement, router, future }) { // Need to use a layout effect here so we are subscribed early enough to // pick up on any render-driven redirects/navigations (useEffect/) let [state, setStateImpl] = React.useState(router.state); let { v7_startTransition } = future || {}; let setState = React.useCallback(newState => { v7_startTransition && startTransitionImpl ? startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState); }, [setStateImpl, v7_startTransition]); React.useLayoutEffect(() => router.subscribe(setState), [router, setState]); let navigator = React.useMemo(() => { return { createHref: router.createHref, encodeLocation: router.encodeLocation, go: n => router.navigate(n), push: (to, state, opts) => router.navigate(to, { state, preventScrollReset: opts?.preventScrollReset }), replace: (to, state, opts) => router.navigate(to, { replace: true, state, preventScrollReset: opts?.preventScrollReset }) }; }, [router]); let basename = router.basename || "/"; let dataRouterContext = React.useMemo(() => ({ router, navigator, static: false, basename }), [router, navigator, basename]); // The fragment and {null} here are important! We need them to keep React 18's // useId happy when we are server-rendering since we may have a