UNPKG

@tanstack/react-router

Version:

Modern and scalable routing for React applications

1 lines • 15.1 kB
{"version":3,"file":"Match.cjs","names":["wrapInNonRouteComponentContext","isServer","React","useRouter","useSelector","ClientOnly","CatchNotFound","CatchBoundary","isNotFound","rootRouteId","ScrollRestoration","matchContext","renderRouteNotFound","ErrorComponent","nonRouteComponentContext"],"sources":["../../src/Match.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { useSelector } from '@tanstack/react-store'\nimport { isNotFound, rootRouteId } from '@tanstack/router-core'\nimport { isServer } from '@tanstack/router-core/isServer'\nimport { CatchBoundary, ErrorComponent } from './CatchBoundary'\nimport { useRouter } from './useRouter'\nimport { CatchNotFound } from './not-found'\nimport { matchContext } from './matchContext'\nimport { renderRouteNotFound } from './renderRouteNotFound'\nimport { ScrollRestoration } from './scroll-restoration'\nimport { ClientOnly } from './ClientOnly'\nimport {\n nonRouteComponentContext,\n wrapInNonRouteComponentContext,\n} from './nonRouteComponentContext'\nimport type {\n AnyRoute,\n AnyRouteMatch,\n RootRouteOptions,\n} from '@tanstack/router-core'\n\nexport function renderPending(\n router: ReturnType<typeof useRouter>,\n route?: AnyRoute,\n) {\n const PendingComponent =\n route?.options.pendingComponent ?? router.options.defaultPendingComponent\n if (!PendingComponent) {\n return null\n }\n\n const pendingElement = <PendingComponent />\n return process.env.NODE_ENV !== 'production'\n ? wrapInNonRouteComponentContext(pendingElement, 'pendingComponent')\n : pendingElement\n}\n\ntype OutletMatchSelection = [\n parentGlobalNotFound: boolean,\n parentNotFoundError: unknown,\n]\n\nconst outletMatchSelectionEqual = (\n a: OutletMatchSelection,\n b: OutletMatchSelection,\n) => a[0] === b[0] && a[1] === b[1]\n\nconst canWrapInSuspense = (\n router: ReturnType<typeof useRouter>,\n route: AnyRoute,\n ssr: AnyRouteMatch['ssr'],\n) =>\n !route.isRoot ||\n (route.options as RootRouteOptions).shellComponent ||\n route.options.wrapInSuspense ||\n ssr === false ||\n ssr === 'data-only' ||\n !((isServer ?? router.isServer) || router.ssr)\n\nexport const Match = React.memo(function MatchImpl({\n routeId,\n}: {\n routeId: string\n}) {\n const router = useRouter()\n\n if (isServer ?? router.isServer) {\n const match = router.stores.byRoute.get(routeId)!.get()!\n return matchView(router, match)\n }\n\n const matchStore = router.stores.getMatchStore(routeId)\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const match = useSelector(matchStore)!\n return matchView(router, match)\n})\n\nfunction matchView(router: ReturnType<typeof useRouter>, match: AnyRouteMatch) {\n const route: AnyRoute = router.routesById[match.routeId]\n\n const pendingElement = renderPending(router, route)\n\n const routeErrorComponent =\n route.options.errorComponent ?? router.options.defaultErrorComponent\n\n const routeOnCatch = route.options.onCatch ?? router.options.defaultOnCatch\n\n const routeNotFoundComponent = route.isRoot\n ? // If it's the root route, use the _notFound option, with fallback to the notFoundRoute's component\n (route.options.notFoundComponent ??\n router.options.notFoundRoute?.options.component)\n : route.options.notFoundComponent\n\n const resolvedNoSsr = match.ssr === false || match.ssr === 'data-only'\n // A root component may render the document itself. Only place its Suspense\n // boundary in pure CSR, inside an explicit shell, or when explicitly opted in.\n const wrapInSuspense =\n canWrapInSuspense(router, route, match.ssr) &&\n (route.options.wrapInSuspense ??\n pendingElement ??\n ((route.options.errorComponent as any)?.preload || resolvedNoSsr))\n\n let content = <MatchInner match={match} />\n if (resolvedNoSsr) {\n content = <ClientOnly fallback={pendingElement}>{content}</ClientOnly>\n }\n if (routeNotFoundComponent) {\n content = (\n <CatchNotFound\n fallback={(error) => {\n error.routeId ??= match.routeId\n if (error.routeId !== match.routeId) {\n throw error\n }\n const notFoundElement = React.createElement(\n routeNotFoundComponent,\n error as any,\n )\n return process.env.NODE_ENV !== 'production'\n ? wrapInNonRouteComponentContext(\n notFoundElement,\n 'notFoundComponent',\n )\n : notFoundElement\n }}\n >\n {content}\n </CatchNotFound>\n )\n }\n if (routeErrorComponent) {\n content = (\n <CatchBoundary\n getResetKey={() => match}\n errorComponent={routeErrorComponent as any}\n onCatch={(error, errorInfo) => {\n // Not-found errors belong to the enclosing not-found boundary.\n if (isNotFound(error)) {\n error.routeId ??= match.routeId\n throw error\n }\n if (process.env.NODE_ENV !== 'production') {\n console.warn(`Warning: Error in route match: ${match.id}`)\n }\n routeOnCatch?.(error, errorInfo)\n }}\n >\n {content}\n </CatchBoundary>\n )\n }\n if (wrapInSuspense) {\n content = (\n <React.Suspense fallback={pendingElement}>{content}</React.Suspense>\n )\n }\n\n const scrollRestoration =\n (isServer ?? router.isServer) &&\n route.parentRoute?.id === rootRouteId &&\n router.options.scrollRestoration ? (\n <ScrollRestoration />\n ) : null\n\n const ShellComponent =\n route.isRoot && (route.options as RootRouteOptions).shellComponent\n\n return ShellComponent ? (\n // The shell and route boundaries must share this match's context.\n <matchContext.Provider value={match.routeId}>\n <ShellComponent>\n {content}\n {scrollRestoration}\n </ShellComponent>\n </matchContext.Provider>\n ) : (\n <matchContext.Provider value={match.routeId}>\n {content}\n {scrollRestoration}\n </matchContext.Provider>\n )\n}\n\nexport const MatchInner = React.memo(function MatchInnerImpl({\n match,\n}: {\n match: AnyRouteMatch\n}): any {\n const router = useRouter()\n const routeId = match.routeId\n const route = router.routesById[routeId] as AnyRoute\n const key = React.useMemo(() => {\n const remountFn =\n route.options.remountDeps ?? router.options.defaultRemountDeps\n const remountDeps = remountFn?.({\n routeId,\n loaderDeps: match.loaderDeps,\n params: match._strictParams,\n search: match._strictSearch,\n })\n return remountDeps ? JSON.stringify(remountDeps) : undefined\n }, [\n routeId,\n match.loaderDeps,\n match._strictParams,\n match._strictSearch,\n route.options.remountDeps,\n router.options.defaultRemountDeps,\n ])\n const out = React.useMemo(() => {\n const Comp = route.options.component ?? router.options.defaultComponent\n return Comp ? <Comp key={key} /> : <Outlet />\n }, [key, route.options.component, router.options.defaultComponent])\n\n if (match.status === 'pending') {\n if (router.ssr && !canWrapInSuspense(router, route, match.ssr)) {\n // Replacing an SSR document root with pending UI would remove <html>.\n // Hydrated matches retain their prior data, so keep rendering it.\n return out\n }\n if (router._tx) {\n throw router._tx[5]\n }\n return renderPending(router, route)\n }\n\n if (match.status === 'notFound') {\n return renderRouteNotFound(router, route, match.error)\n }\n\n if (match.status === 'error') {\n if (isServer ?? router.isServer) {\n const RouteErrorComponent =\n (route.options.errorComponent ??\n router.options.defaultErrorComponent) ||\n ErrorComponent\n const errorElement = (\n <RouteErrorComponent\n error={match.error}\n reset={undefined as any}\n info={{\n componentStack: '',\n }}\n />\n )\n return process.env.NODE_ENV !== 'production'\n ? wrapInNonRouteComponentContext(errorElement, 'errorComponent')\n : errorElement\n }\n throw match.error\n }\n\n return out\n})\n\n/**\n * Render the next child match in the route tree. Typically used inside\n * a route component to render nested routes.\n *\n * @link https://tanstack.com/router/latest/docs/framework/react/api/router/outletComponent\n */\nexport const Outlet = React.memo(function OutletImpl() {\n if (process.env.NODE_ENV !== 'production') {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const nonRouteComponent = React.useContext(nonRouteComponentContext!)\n if (nonRouteComponent) {\n console.warn(\n `Warning: An <Outlet /> was rendered inside a ${nonRouteComponent}. <Outlet /> should only be rendered inside a route component.`,\n )\n }\n }\n\n const router = useRouter()\n const routeId = React.useContext(matchContext)!\n\n let parentGlobalNotFound: boolean\n let parentNotFoundError: unknown\n let childRouteId: string | undefined\n\n if (isServer ?? router.isServer) {\n const matches = router.stores.matches.get()\n const parentIndex = matches.findIndex((match) => match.routeId === routeId)\n const parentMatch = matches[parentIndex]!\n parentGlobalNotFound = !!parentMatch._notFound\n parentNotFoundError = parentMatch.error\n childRouteId = matches[parentIndex + 1]?.routeId\n } else {\n const parentMatchStore = router.stores.getMatchStore(routeId)\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n ;[parentGlobalNotFound, parentNotFoundError] = useSelector(\n parentMatchStore,\n (match): OutletMatchSelection => [!!match!._notFound, match!.error],\n { compare: outletMatchSelectionEqual },\n )\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n childRouteId = useSelector(router.stores.ids, (ids) => {\n return ids[ids.indexOf(routeId) + 1]\n })\n }\n\n if (parentGlobalNotFound) {\n return renderRouteNotFound(\n router,\n router.routesById[routeId],\n parentNotFoundError,\n )\n }\n\n if (!childRouteId) {\n return null\n }\n\n const nextMatch = <Match routeId={childRouteId} />\n\n if (routeId === rootRouteId) {\n return (\n <React.Suspense fallback={renderPending(router)}>\n {nextMatch}\n </React.Suspense>\n )\n }\n\n return nextMatch\n})\n"],"mappings":"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"}