@tanstack/react-router 1.46.2 → 1.46.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Match.cjs +30 -19
- package/dist/cjs/Match.cjs.map +1 -1
- package/dist/esm/Match.js +30 -19
- package/dist/esm/Match.js.map +1 -1
- package/package.json +1 -1
- package/src/Match.tsx +39 -28
package/dist/cjs/Match.cjs
CHANGED
|
@@ -123,6 +123,36 @@ const MatchInner = React__namespace.memo(function MatchInnerImpl({
|
|
|
123
123
|
const Comp = route.options.component ?? router.options.defaultComponent;
|
|
124
124
|
return Comp ? /* @__PURE__ */ jsxRuntime.jsx(Comp, {}, matchId) : /* @__PURE__ */ jsxRuntime.jsx(Outlet, {});
|
|
125
125
|
}, [matchId, route.options.component, router.options.defaultComponent]);
|
|
126
|
+
React__namespace.useEffect(() => {
|
|
127
|
+
if (match.status === "pending") {
|
|
128
|
+
const pendingMinMs = route.options.pendingMinMs ?? router.options.defaultPendingMinMs;
|
|
129
|
+
if (pendingMinMs && !match.minPendingPromise) {
|
|
130
|
+
if (!router.isServer) {
|
|
131
|
+
const minPendingPromise = utils.createControlledPromise();
|
|
132
|
+
router.updateMatch(match.id, (prev) => ({
|
|
133
|
+
...prev,
|
|
134
|
+
minPendingPromise
|
|
135
|
+
}));
|
|
136
|
+
const id = setTimeout(() => {
|
|
137
|
+
minPendingPromise.resolve();
|
|
138
|
+
router.updateMatch(match.id, (prev) => ({
|
|
139
|
+
...prev,
|
|
140
|
+
minPendingPromise: void 0
|
|
141
|
+
}));
|
|
142
|
+
}, pendingMinMs);
|
|
143
|
+
return () => clearTimeout(id);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return void 0;
|
|
148
|
+
}, [
|
|
149
|
+
match.id,
|
|
150
|
+
match.loadPromise,
|
|
151
|
+
match.minPendingPromise,
|
|
152
|
+
match.status,
|
|
153
|
+
route.options.pendingMinMs,
|
|
154
|
+
router
|
|
155
|
+
]);
|
|
126
156
|
const RouteErrorComponent = (route.options.errorComponent ?? router.options.defaultErrorComponent) || CatchBoundary.ErrorComponent;
|
|
127
157
|
if (match.status === "notFound") {
|
|
128
158
|
let error;
|
|
@@ -159,25 +189,6 @@ const MatchInner = React__namespace.memo(function MatchInnerImpl({
|
|
|
159
189
|
}
|
|
160
190
|
}
|
|
161
191
|
if (match.status === "pending") {
|
|
162
|
-
const pendingMinMs = route.options.pendingMinMs ?? router.options.defaultPendingMinMs;
|
|
163
|
-
if (pendingMinMs && !match.minPendingPromise) {
|
|
164
|
-
if (!router.isServer) {
|
|
165
|
-
const minPendingPromise = utils.createControlledPromise();
|
|
166
|
-
Promise.resolve().then(() => {
|
|
167
|
-
router.updateMatch(match.id, (prev) => ({
|
|
168
|
-
...prev,
|
|
169
|
-
minPendingPromise
|
|
170
|
-
}));
|
|
171
|
-
});
|
|
172
|
-
setTimeout(() => {
|
|
173
|
-
minPendingPromise.resolve();
|
|
174
|
-
router.updateMatch(match.id, (prev) => ({
|
|
175
|
-
...prev,
|
|
176
|
-
minPendingPromise: void 0
|
|
177
|
-
}));
|
|
178
|
-
}, pendingMinMs);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
192
|
throw match.loadPromise;
|
|
182
193
|
}
|
|
183
194
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
package/dist/cjs/Match.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Match.cjs","sources":["../../src/Match.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport invariant from 'tiny-invariant'\nimport warning from 'tiny-warning'\nimport { CatchBoundary, ErrorComponent } from './CatchBoundary'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\nimport { createControlledPromise, pick } from './utils'\nimport { CatchNotFound, isNotFound } from './not-found'\nimport { isRedirect } from './redirects'\nimport { matchContext } from './matchContext'\nimport { defaultDeserializeError, isServerSideError } from './isServerSideError'\nimport { SafeFragment } from './SafeFragment'\nimport { renderRouteNotFound } from './renderRouteNotFound'\nimport { rootRouteId } from './root'\nimport type { AnyRoute } from './route'\n\nexport const Match = React.memo(function MatchImpl({\n matchId,\n}: {\n matchId: string\n}) {\n const router = useRouter()\n const routeId = useRouterState({\n select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,\n })\n\n invariant(\n routeId,\n `Could not find routeId for matchId \"${matchId}\". Please file an issue!`,\n )\n\n const route: AnyRoute = router.routesById[routeId]\n\n const PendingComponent =\n route.options.pendingComponent ?? router.options.defaultPendingComponent\n\n const pendingElement = PendingComponent ? <PendingComponent /> : null\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 globalNotFound 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 ResolvedSuspenseBoundary =\n // If we're on the root route, allow forcefully wrapping in suspense\n (!route.isRoot || route.options.wrapInSuspense) &&\n (route.options.wrapInSuspense ??\n PendingComponent ??\n (route.options.errorComponent as any)?.preload)\n ? React.Suspense\n : SafeFragment\n\n const ResolvedCatchBoundary = routeErrorComponent\n ? CatchBoundary\n : SafeFragment\n\n const ResolvedNotFoundBoundary = routeNotFoundComponent\n ? CatchNotFound\n : SafeFragment\n\n const resetKey = useRouterState({\n select: (s) => s.loadedAt,\n })\n\n return (\n <matchContext.Provider value={matchId}>\n <ResolvedSuspenseBoundary fallback={pendingElement}>\n <ResolvedCatchBoundary\n getResetKey={() => resetKey}\n errorComponent={routeErrorComponent || ErrorComponent}\n onCatch={(error, errorInfo) => {\n // Forward not found errors (we don't want to show the error component for these)\n if (isNotFound(error)) throw error\n warning(false, `Error in route match: ${matchId}`)\n routeOnCatch?.(error, errorInfo)\n }}\n >\n <ResolvedNotFoundBoundary\n fallback={(error) => {\n // If the current not found handler doesn't exist or it has a\n // route ID which doesn't match the current route, rethrow the error\n if (\n !routeNotFoundComponent ||\n (error.routeId && error.routeId !== routeId) ||\n (!error.routeId && !route.isRoot)\n )\n throw error\n\n return React.createElement(routeNotFoundComponent, error as any)\n }}\n >\n <MatchInner matchId={matchId} />\n </ResolvedNotFoundBoundary>\n </ResolvedCatchBoundary>\n </ResolvedSuspenseBoundary>\n </matchContext.Provider>\n )\n})\n\nexport const MatchInner = React.memo(function MatchInnerImpl({\n matchId,\n}: {\n matchId: string\n}): any {\n const router = useRouter()\n const routeId = useRouterState({\n select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,\n })\n\n const route = router.routesById[routeId]!\n\n const matchIndex = useRouterState({\n select: (s) => {\n return s.matches.findIndex((d) => d.id === matchId)\n },\n })\n\n const match = useRouterState({\n select: (s) => {\n const match = s.matches[matchIndex]!\n return pick(match, [\n 'id',\n 'status',\n 'error',\n 'loadPromise',\n 'minPendingPromise',\n ])\n },\n })\n\n const out = React.useMemo(() => {\n const Comp = route.options.component ?? router.options.defaultComponent\n return Comp ? <Comp key={matchId} /> : <Outlet />\n }, [matchId, route.options.component, router.options.defaultComponent])\n\n // function useChangedDiff(value: any) {\n // const ref = React.useRef(value)\n // const changed = ref.current !== value\n // if (changed) {\n // console.log(\n // 'Changed:',\n // value,\n // Object.fromEntries(\n // Object.entries(value).filter(\n // ([key, val]) => val !== ref.current[key],\n // ),\n // ),\n // )\n // }\n // ref.current = value\n // }\n\n // useChangedDiff(match)\n\n const RouteErrorComponent =\n (route.options.errorComponent ?? router.options.defaultErrorComponent) ||\n ErrorComponent\n\n if (match.status === 'notFound') {\n let error: unknown\n if (isServerSideError(match.error)) {\n const deserializeError =\n router.options.errorSerializer?.deserialize ?? defaultDeserializeError\n\n error = deserializeError(match.error.data)\n } else {\n error = match.error\n }\n\n invariant(isNotFound(error), 'Expected a notFound error')\n\n return renderRouteNotFound(router, route, error)\n }\n\n if (match.status === 'redirected') {\n // Redirects should be handled by the router transition. If we happen to\n // encounter a redirect here, it's a bug. Let's warn, but render nothing.\n invariant(isRedirect(match.error), 'Expected a redirect error')\n\n // warning(\n // false,\n // 'Tried to render a redirected route match! This is a weird circumstance, please file an issue!',\n // )\n throw match.loadPromise\n }\n\n if (match.status === 'error') {\n // If we're on the server, we need to use React's new and super\n // wonky api for throwing errors from a server side render inside\n // of a suspense boundary. This is the only way to get\n // renderToPipeableStream to not hang indefinitely.\n // We'll serialize the error and rethrow it on the client.\n if (router.isServer) {\n return (\n <RouteErrorComponent\n error={match.error}\n info={{\n componentStack: '',\n }}\n />\n )\n }\n\n if (isServerSideError(match.error)) {\n const deserializeError =\n router.options.errorSerializer?.deserialize ?? defaultDeserializeError\n throw deserializeError(match.error.data)\n } else {\n throw match.error\n }\n }\n\n if (match.status === 'pending') {\n // We're pending, and if we have a minPendingMs, we need to wait for it\n const pendingMinMs =\n route.options.pendingMinMs ?? router.options.defaultPendingMinMs\n\n if (pendingMinMs && !match.minPendingPromise) {\n // Create a promise that will resolve after the minPendingMs\n if (!router.isServer) {\n const minPendingPromise = createControlledPromise<void>()\n\n Promise.resolve().then(() => {\n router.updateMatch(match.id, (prev) => ({\n ...prev,\n minPendingPromise,\n }))\n })\n\n setTimeout(() => {\n minPendingPromise.resolve()\n\n // We've handled the minPendingPromise, so we can delete it\n router.updateMatch(match.id, (prev) => ({\n ...prev,\n minPendingPromise: undefined,\n }))\n }, pendingMinMs)\n }\n }\n\n throw match.loadPromise\n }\n\n return (\n <>\n {out}\n {router.AfterEachMatch ? (\n <router.AfterEachMatch match={match} matchIndex={matchIndex} />\n ) : null}\n </>\n )\n})\n\nexport const Outlet = React.memo(function OutletImpl() {\n const router = useRouter()\n const matchId = React.useContext(matchContext)\n const routeId = useRouterState({\n select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,\n })\n\n const route = router.routesById[routeId]!\n\n const { parentGlobalNotFound } = useRouterState({\n select: (s) => {\n const matches = s.matches\n const parentMatch = matches.find((d) => d.id === matchId)\n invariant(\n parentMatch,\n `Could not find parent match for matchId \"${matchId}\"`,\n )\n return {\n parentGlobalNotFound: parentMatch.globalNotFound,\n }\n },\n })\n\n const childMatchId = useRouterState({\n select: (s) => {\n const matches = s.matches\n const index = matches.findIndex((d) => d.id === matchId)\n return matches[index + 1]?.id\n },\n })\n\n if (parentGlobalNotFound) {\n return renderRouteNotFound(router, route, undefined)\n }\n\n if (!childMatchId) {\n return null\n }\n\n const nextMatch = <Match matchId={childMatchId} />\n\n const pendingElement = router.options.defaultPendingComponent ? (\n <router.options.defaultPendingComponent />\n ) : null\n\n if (matchId === rootRouteId) {\n return (\n <React.Suspense fallback={pendingElement}>{nextMatch}</React.Suspense>\n )\n }\n\n return nextMatch\n})\n"],"names":["match"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkBO;AAA4C;AAEnD;;AAGE;AACA;AAA+B;;AACd;AAAyC;AAAA;AAG1D;AAAA;AACE;AAC8C;AAG1C;AAEN;AAGA;AAEA;AAGA;AAEA;AAAqC;AAAA;AAGK;AAGpC;AAAA;AAAA;AAOA;AAEA;AAIA;AAIN;AAAgC;AACb;AAIjB;AAEI;AAAC;AAAA;AACoB;AACoB;AAGjC;AACI;AACR;AAAsB;AACxB;AAEA;AAAC;AAAA;AAKK;AAIM;AAED;AAAwD;AACjE;AAE8B;AAAA;AAChC;AAAA;AAKV;AAEO;AAAsD;AAE7D;;AAGE;AACA;AAA+B;;AACd;AAAyC;AAAA;AAGpD;AAEN;AAAkC;AAE9B;AAAkD;AACpD;AAGF;AAA6B;AAEnBA;AACN;AAAmB;AACjB;AACA;AACA;AACA;AACA;AACD;AACH;AAGI;AACJ;AACA;AAA+C;AAsBjD;AAII;AACE;AACA;AACF;AAGQ;AAAiC;AAEzC;AAAc;AAGN;AAEH;AAAwC;AAG7C;AAGF;AAMA;AAAY;AAGV;AAMF;AAEI;AAAA;AAAC;AAAA;AACc;AACP;AACY;AAClB;AAAA;AAAA;AAKF;AACF;AAEM;AAAiC;AAEvC;AAAY;AACd;AAGE;AAEF;AAGI;AAEE;AACF;AAEQ;AACN;AAAwC;AACnC;AACH;AACA;AAGJ;AACE;AAGA;AAAwC;AACnC;AACgB;AACnB;AACW;AACjB;AAGF;AAAY;AAGd;AAEK;AAAA;AAGG;AAGV;AAEO;AACL;AACM;AACN;AAA+B;;AACd;AAAyC;AAAA;AAGpD;AAEA;AAA0C;AAE5C;AACA;AACA;AAAA;AACE;AACmD;AAE9C;AAAA;AAC6B;AAAA;AAEtC;AAGF;AAAoC;;AAEhC;AACA;AACO;AAAoB;AAC7B;AAGF;AACS;AAA4C;AAGrD;AACS;AAAA;AAGT;AAEM;AAIN;AACE;AACuD;AAIlD;AACT;;;;"}
|
|
1
|
+
{"version":3,"file":"Match.cjs","sources":["../../src/Match.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport invariant from 'tiny-invariant'\nimport warning from 'tiny-warning'\nimport { CatchBoundary, ErrorComponent } from './CatchBoundary'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\nimport { createControlledPromise, pick } from './utils'\nimport { CatchNotFound, isNotFound } from './not-found'\nimport { isRedirect } from './redirects'\nimport { matchContext } from './matchContext'\nimport { defaultDeserializeError, isServerSideError } from './isServerSideError'\nimport { SafeFragment } from './SafeFragment'\nimport { renderRouteNotFound } from './renderRouteNotFound'\nimport { rootRouteId } from './root'\nimport type { AnyRoute } from './route'\n\nexport const Match = React.memo(function MatchImpl({\n matchId,\n}: {\n matchId: string\n}) {\n const router = useRouter()\n const routeId = useRouterState({\n select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,\n })\n\n invariant(\n routeId,\n `Could not find routeId for matchId \"${matchId}\". Please file an issue!`,\n )\n\n const route: AnyRoute = router.routesById[routeId]\n\n const PendingComponent =\n route.options.pendingComponent ?? router.options.defaultPendingComponent\n\n const pendingElement = PendingComponent ? <PendingComponent /> : null\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 globalNotFound 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 ResolvedSuspenseBoundary =\n // If we're on the root route, allow forcefully wrapping in suspense\n (!route.isRoot || route.options.wrapInSuspense) &&\n (route.options.wrapInSuspense ??\n PendingComponent ??\n (route.options.errorComponent as any)?.preload)\n ? React.Suspense\n : SafeFragment\n\n const ResolvedCatchBoundary = routeErrorComponent\n ? CatchBoundary\n : SafeFragment\n\n const ResolvedNotFoundBoundary = routeNotFoundComponent\n ? CatchNotFound\n : SafeFragment\n\n const resetKey = useRouterState({\n select: (s) => s.loadedAt,\n })\n\n return (\n <matchContext.Provider value={matchId}>\n <ResolvedSuspenseBoundary fallback={pendingElement}>\n <ResolvedCatchBoundary\n getResetKey={() => resetKey}\n errorComponent={routeErrorComponent || ErrorComponent}\n onCatch={(error, errorInfo) => {\n // Forward not found errors (we don't want to show the error component for these)\n if (isNotFound(error)) throw error\n warning(false, `Error in route match: ${matchId}`)\n routeOnCatch?.(error, errorInfo)\n }}\n >\n <ResolvedNotFoundBoundary\n fallback={(error) => {\n // If the current not found handler doesn't exist or it has a\n // route ID which doesn't match the current route, rethrow the error\n if (\n !routeNotFoundComponent ||\n (error.routeId && error.routeId !== routeId) ||\n (!error.routeId && !route.isRoot)\n )\n throw error\n\n return React.createElement(routeNotFoundComponent, error as any)\n }}\n >\n <MatchInner matchId={matchId} />\n </ResolvedNotFoundBoundary>\n </ResolvedCatchBoundary>\n </ResolvedSuspenseBoundary>\n </matchContext.Provider>\n )\n})\n\nexport const MatchInner = React.memo(function MatchInnerImpl({\n matchId,\n}: {\n matchId: string\n}): any {\n const router = useRouter()\n const routeId = useRouterState({\n select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,\n })\n\n const route = router.routesById[routeId]!\n\n const matchIndex = useRouterState({\n select: (s) => {\n return s.matches.findIndex((d) => d.id === matchId)\n },\n })\n\n const match = useRouterState({\n select: (s) => {\n const match = s.matches[matchIndex]!\n return pick(match, [\n 'id',\n 'status',\n 'error',\n 'loadPromise',\n 'minPendingPromise',\n ])\n },\n })\n\n const out = React.useMemo(() => {\n const Comp = route.options.component ?? router.options.defaultComponent\n return Comp ? <Comp key={matchId} /> : <Outlet />\n }, [matchId, route.options.component, router.options.defaultComponent])\n\n React.useEffect(() => {\n if (match.status === 'pending') {\n // We're pending, and if we have a minPendingMs, we need to wait for it\n const pendingMinMs =\n route.options.pendingMinMs ?? router.options.defaultPendingMinMs\n\n if (pendingMinMs && !match.minPendingPromise) {\n // Create a promise that will resolve after the minPendingMs\n if (!router.isServer) {\n const minPendingPromise = createControlledPromise<void>()\n\n router.updateMatch(match.id, (prev) => ({\n ...prev,\n minPendingPromise,\n }))\n\n const id = setTimeout(() => {\n minPendingPromise.resolve()\n\n // We've handled the minPendingPromise, so we can delete it\n router.updateMatch(match.id, (prev) => ({\n ...prev,\n minPendingPromise: undefined,\n }))\n }, pendingMinMs)\n return () => clearTimeout(id)\n }\n }\n }\n return undefined\n }, [\n match.id,\n match.loadPromise,\n match.minPendingPromise,\n match.status,\n route.options.pendingMinMs,\n router,\n ])\n\n // function useChangedDiff(value: any) {\n // const ref = React.useRef(value)\n // const changed = ref.current !== value\n // if (changed) {\n // console.log(\n // 'Changed:',\n // value,\n // Object.fromEntries(\n // Object.entries(value).filter(\n // ([key, val]) => val !== ref.current[key],\n // ),\n // ),\n // )\n // }\n // ref.current = value\n // }\n\n // useChangedDiff(match)\n\n const RouteErrorComponent =\n (route.options.errorComponent ?? router.options.defaultErrorComponent) ||\n ErrorComponent\n\n if (match.status === 'notFound') {\n let error: unknown\n if (isServerSideError(match.error)) {\n const deserializeError =\n router.options.errorSerializer?.deserialize ?? defaultDeserializeError\n\n error = deserializeError(match.error.data)\n } else {\n error = match.error\n }\n\n invariant(isNotFound(error), 'Expected a notFound error')\n\n return renderRouteNotFound(router, route, error)\n }\n\n if (match.status === 'redirected') {\n // Redirects should be handled by the router transition. If we happen to\n // encounter a redirect here, it's a bug. Let's warn, but render nothing.\n invariant(isRedirect(match.error), 'Expected a redirect error')\n\n // warning(\n // false,\n // 'Tried to render a redirected route match! This is a weird circumstance, please file an issue!',\n // )\n throw match.loadPromise\n }\n\n if (match.status === 'error') {\n // If we're on the server, we need to use React's new and super\n // wonky api for throwing errors from a server side render inside\n // of a suspense boundary. This is the only way to get\n // renderToPipeableStream to not hang indefinitely.\n // We'll serialize the error and rethrow it on the client.\n if (router.isServer) {\n return (\n <RouteErrorComponent\n error={match.error}\n info={{\n componentStack: '',\n }}\n />\n )\n }\n\n if (isServerSideError(match.error)) {\n const deserializeError =\n router.options.errorSerializer?.deserialize ?? defaultDeserializeError\n throw deserializeError(match.error.data)\n } else {\n throw match.error\n }\n }\n\n if (match.status === 'pending') {\n throw match.loadPromise\n }\n\n return (\n <>\n {out}\n {router.AfterEachMatch ? (\n <router.AfterEachMatch match={match} matchIndex={matchIndex} />\n ) : null}\n </>\n )\n})\n\nexport const Outlet = React.memo(function OutletImpl() {\n const router = useRouter()\n const matchId = React.useContext(matchContext)\n const routeId = useRouterState({\n select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,\n })\n\n const route = router.routesById[routeId]!\n\n const { parentGlobalNotFound } = useRouterState({\n select: (s) => {\n const matches = s.matches\n const parentMatch = matches.find((d) => d.id === matchId)\n invariant(\n parentMatch,\n `Could not find parent match for matchId \"${matchId}\"`,\n )\n return {\n parentGlobalNotFound: parentMatch.globalNotFound,\n }\n },\n })\n\n const childMatchId = useRouterState({\n select: (s) => {\n const matches = s.matches\n const index = matches.findIndex((d) => d.id === matchId)\n return matches[index + 1]?.id\n },\n })\n\n if (parentGlobalNotFound) {\n return renderRouteNotFound(router, route, undefined)\n }\n\n if (!childMatchId) {\n return null\n }\n\n const nextMatch = <Match matchId={childMatchId} />\n\n const pendingElement = router.options.defaultPendingComponent ? (\n <router.options.defaultPendingComponent />\n ) : null\n\n if (matchId === rootRouteId) {\n return (\n <React.Suspense fallback={pendingElement}>{nextMatch}</React.Suspense>\n )\n }\n\n return nextMatch\n})\n"],"names":["match","React"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkBO;AAA4C;AAEnD;;AAGE;AACA;AAA+B;;AACd;AAAyC;AAAA;AAG1D;AAAA;AACE;AAC8C;AAG1C;AAEN;AAGA;AAEA;AAGA;AAEA;AAAqC;AAAA;AAGK;AAGpC;AAAA;AAAA;AAOA;AAEA;AAIA;AAIN;AAAgC;AACb;AAIjB;AAEI;AAAC;AAAA;AACoB;AACoB;AAGjC;AACI;AACR;AAAsB;AACxB;AAEA;AAAC;AAAA;AAKK;AAIM;AAED;AAAwD;AACjE;AAE8B;AAAA;AAChC;AAAA;AAKV;AAEO;AAAsD;AAE7D;;AAGE;AACA;AAA+B;;AACd;AAAyC;AAAA;AAGpD;AAEN;AAAkC;AAE9B;AAAkD;AACpD;AAGF;AAA6B;AAEnBA;AACN;AAAmB;AACjB;AACA;AACA;AACA;AACA;AACD;AACH;AAGI;AACJ;AACA;AAA+C;AAGjDC;AACM;AAEF;AAGI;AAEE;AACF;AAEA;AAAwC;AACnC;AACH;AAGI;AACJ;AAGA;AAAwC;AACnC;AACgB;AACnB;AAEG;AAAqB;AAC9B;AACF;AAEK;AAAA;AACN;AACK;AACA;AACA;AACA;AACQ;AACd;AAsBF;AAII;AACE;AACA;AACF;AAGQ;AAAiC;AAEzC;AAAc;AAGN;AAEH;AAAwC;AAG7C;AAGF;AAMA;AAAY;AAGV;AAMF;AAEI;AAAA;AAAC;AAAA;AACc;AACP;AACY;AAClB;AAAA;AAAA;AAKF;AACF;AAEM;AAAiC;AAEvC;AAAY;AACd;AAGE;AACF;AAAY;AAGd;AAEK;AAAA;AAGG;AAGV;AAEO;AACL;AACM;AACN;AAA+B;;AACd;AAAyC;AAAA;AAGpD;AAEA;AAA0C;AAE5C;AACA;AACA;AAAA;AACE;AACmD;AAE9C;AAAA;AAC6B;AAAA;AAEtC;AAGF;AAAoC;;AAEhC;AACA;AACO;AAAoB;AAC7B;AAGF;AACS;AAA4C;AAGrD;AACS;AAAA;AAGT;AAEM;AAIN;AACE;AACuD;AAIlD;AACT;;;;"}
|
package/dist/esm/Match.js
CHANGED
|
@@ -104,6 +104,36 @@ const MatchInner = React.memo(function MatchInnerImpl({
|
|
|
104
104
|
const Comp = route.options.component ?? router.options.defaultComponent;
|
|
105
105
|
return Comp ? /* @__PURE__ */ jsx(Comp, {}, matchId) : /* @__PURE__ */ jsx(Outlet, {});
|
|
106
106
|
}, [matchId, route.options.component, router.options.defaultComponent]);
|
|
107
|
+
React.useEffect(() => {
|
|
108
|
+
if (match.status === "pending") {
|
|
109
|
+
const pendingMinMs = route.options.pendingMinMs ?? router.options.defaultPendingMinMs;
|
|
110
|
+
if (pendingMinMs && !match.minPendingPromise) {
|
|
111
|
+
if (!router.isServer) {
|
|
112
|
+
const minPendingPromise = createControlledPromise();
|
|
113
|
+
router.updateMatch(match.id, (prev) => ({
|
|
114
|
+
...prev,
|
|
115
|
+
minPendingPromise
|
|
116
|
+
}));
|
|
117
|
+
const id = setTimeout(() => {
|
|
118
|
+
minPendingPromise.resolve();
|
|
119
|
+
router.updateMatch(match.id, (prev) => ({
|
|
120
|
+
...prev,
|
|
121
|
+
minPendingPromise: void 0
|
|
122
|
+
}));
|
|
123
|
+
}, pendingMinMs);
|
|
124
|
+
return () => clearTimeout(id);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return void 0;
|
|
129
|
+
}, [
|
|
130
|
+
match.id,
|
|
131
|
+
match.loadPromise,
|
|
132
|
+
match.minPendingPromise,
|
|
133
|
+
match.status,
|
|
134
|
+
route.options.pendingMinMs,
|
|
135
|
+
router
|
|
136
|
+
]);
|
|
107
137
|
const RouteErrorComponent = (route.options.errorComponent ?? router.options.defaultErrorComponent) || ErrorComponent;
|
|
108
138
|
if (match.status === "notFound") {
|
|
109
139
|
let error;
|
|
@@ -140,25 +170,6 @@ const MatchInner = React.memo(function MatchInnerImpl({
|
|
|
140
170
|
}
|
|
141
171
|
}
|
|
142
172
|
if (match.status === "pending") {
|
|
143
|
-
const pendingMinMs = route.options.pendingMinMs ?? router.options.defaultPendingMinMs;
|
|
144
|
-
if (pendingMinMs && !match.minPendingPromise) {
|
|
145
|
-
if (!router.isServer) {
|
|
146
|
-
const minPendingPromise = createControlledPromise();
|
|
147
|
-
Promise.resolve().then(() => {
|
|
148
|
-
router.updateMatch(match.id, (prev) => ({
|
|
149
|
-
...prev,
|
|
150
|
-
minPendingPromise
|
|
151
|
-
}));
|
|
152
|
-
});
|
|
153
|
-
setTimeout(() => {
|
|
154
|
-
minPendingPromise.resolve();
|
|
155
|
-
router.updateMatch(match.id, (prev) => ({
|
|
156
|
-
...prev,
|
|
157
|
-
minPendingPromise: void 0
|
|
158
|
-
}));
|
|
159
|
-
}, pendingMinMs);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
173
|
throw match.loadPromise;
|
|
163
174
|
}
|
|
164
175
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
package/dist/esm/Match.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Match.js","sources":["../../src/Match.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport invariant from 'tiny-invariant'\nimport warning from 'tiny-warning'\nimport { CatchBoundary, ErrorComponent } from './CatchBoundary'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\nimport { createControlledPromise, pick } from './utils'\nimport { CatchNotFound, isNotFound } from './not-found'\nimport { isRedirect } from './redirects'\nimport { matchContext } from './matchContext'\nimport { defaultDeserializeError, isServerSideError } from './isServerSideError'\nimport { SafeFragment } from './SafeFragment'\nimport { renderRouteNotFound } from './renderRouteNotFound'\nimport { rootRouteId } from './root'\nimport type { AnyRoute } from './route'\n\nexport const Match = React.memo(function MatchImpl({\n matchId,\n}: {\n matchId: string\n}) {\n const router = useRouter()\n const routeId = useRouterState({\n select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,\n })\n\n invariant(\n routeId,\n `Could not find routeId for matchId \"${matchId}\". Please file an issue!`,\n )\n\n const route: AnyRoute = router.routesById[routeId]\n\n const PendingComponent =\n route.options.pendingComponent ?? router.options.defaultPendingComponent\n\n const pendingElement = PendingComponent ? <PendingComponent /> : null\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 globalNotFound 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 ResolvedSuspenseBoundary =\n // If we're on the root route, allow forcefully wrapping in suspense\n (!route.isRoot || route.options.wrapInSuspense) &&\n (route.options.wrapInSuspense ??\n PendingComponent ??\n (route.options.errorComponent as any)?.preload)\n ? React.Suspense\n : SafeFragment\n\n const ResolvedCatchBoundary = routeErrorComponent\n ? CatchBoundary\n : SafeFragment\n\n const ResolvedNotFoundBoundary = routeNotFoundComponent\n ? CatchNotFound\n : SafeFragment\n\n const resetKey = useRouterState({\n select: (s) => s.loadedAt,\n })\n\n return (\n <matchContext.Provider value={matchId}>\n <ResolvedSuspenseBoundary fallback={pendingElement}>\n <ResolvedCatchBoundary\n getResetKey={() => resetKey}\n errorComponent={routeErrorComponent || ErrorComponent}\n onCatch={(error, errorInfo) => {\n // Forward not found errors (we don't want to show the error component for these)\n if (isNotFound(error)) throw error\n warning(false, `Error in route match: ${matchId}`)\n routeOnCatch?.(error, errorInfo)\n }}\n >\n <ResolvedNotFoundBoundary\n fallback={(error) => {\n // If the current not found handler doesn't exist or it has a\n // route ID which doesn't match the current route, rethrow the error\n if (\n !routeNotFoundComponent ||\n (error.routeId && error.routeId !== routeId) ||\n (!error.routeId && !route.isRoot)\n )\n throw error\n\n return React.createElement(routeNotFoundComponent, error as any)\n }}\n >\n <MatchInner matchId={matchId} />\n </ResolvedNotFoundBoundary>\n </ResolvedCatchBoundary>\n </ResolvedSuspenseBoundary>\n </matchContext.Provider>\n )\n})\n\nexport const MatchInner = React.memo(function MatchInnerImpl({\n matchId,\n}: {\n matchId: string\n}): any {\n const router = useRouter()\n const routeId = useRouterState({\n select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,\n })\n\n const route = router.routesById[routeId]!\n\n const matchIndex = useRouterState({\n select: (s) => {\n return s.matches.findIndex((d) => d.id === matchId)\n },\n })\n\n const match = useRouterState({\n select: (s) => {\n const match = s.matches[matchIndex]!\n return pick(match, [\n 'id',\n 'status',\n 'error',\n 'loadPromise',\n 'minPendingPromise',\n ])\n },\n })\n\n const out = React.useMemo(() => {\n const Comp = route.options.component ?? router.options.defaultComponent\n return Comp ? <Comp key={matchId} /> : <Outlet />\n }, [matchId, route.options.component, router.options.defaultComponent])\n\n // function useChangedDiff(value: any) {\n // const ref = React.useRef(value)\n // const changed = ref.current !== value\n // if (changed) {\n // console.log(\n // 'Changed:',\n // value,\n // Object.fromEntries(\n // Object.entries(value).filter(\n // ([key, val]) => val !== ref.current[key],\n // ),\n // ),\n // )\n // }\n // ref.current = value\n // }\n\n // useChangedDiff(match)\n\n const RouteErrorComponent =\n (route.options.errorComponent ?? router.options.defaultErrorComponent) ||\n ErrorComponent\n\n if (match.status === 'notFound') {\n let error: unknown\n if (isServerSideError(match.error)) {\n const deserializeError =\n router.options.errorSerializer?.deserialize ?? defaultDeserializeError\n\n error = deserializeError(match.error.data)\n } else {\n error = match.error\n }\n\n invariant(isNotFound(error), 'Expected a notFound error')\n\n return renderRouteNotFound(router, route, error)\n }\n\n if (match.status === 'redirected') {\n // Redirects should be handled by the router transition. If we happen to\n // encounter a redirect here, it's a bug. Let's warn, but render nothing.\n invariant(isRedirect(match.error), 'Expected a redirect error')\n\n // warning(\n // false,\n // 'Tried to render a redirected route match! This is a weird circumstance, please file an issue!',\n // )\n throw match.loadPromise\n }\n\n if (match.status === 'error') {\n // If we're on the server, we need to use React's new and super\n // wonky api for throwing errors from a server side render inside\n // of a suspense boundary. This is the only way to get\n // renderToPipeableStream to not hang indefinitely.\n // We'll serialize the error and rethrow it on the client.\n if (router.isServer) {\n return (\n <RouteErrorComponent\n error={match.error}\n info={{\n componentStack: '',\n }}\n />\n )\n }\n\n if (isServerSideError(match.error)) {\n const deserializeError =\n router.options.errorSerializer?.deserialize ?? defaultDeserializeError\n throw deserializeError(match.error.data)\n } else {\n throw match.error\n }\n }\n\n if (match.status === 'pending') {\n // We're pending, and if we have a minPendingMs, we need to wait for it\n const pendingMinMs =\n route.options.pendingMinMs ?? router.options.defaultPendingMinMs\n\n if (pendingMinMs && !match.minPendingPromise) {\n // Create a promise that will resolve after the minPendingMs\n if (!router.isServer) {\n const minPendingPromise = createControlledPromise<void>()\n\n Promise.resolve().then(() => {\n router.updateMatch(match.id, (prev) => ({\n ...prev,\n minPendingPromise,\n }))\n })\n\n setTimeout(() => {\n minPendingPromise.resolve()\n\n // We've handled the minPendingPromise, so we can delete it\n router.updateMatch(match.id, (prev) => ({\n ...prev,\n minPendingPromise: undefined,\n }))\n }, pendingMinMs)\n }\n }\n\n throw match.loadPromise\n }\n\n return (\n <>\n {out}\n {router.AfterEachMatch ? (\n <router.AfterEachMatch match={match} matchIndex={matchIndex} />\n ) : null}\n </>\n )\n})\n\nexport const Outlet = React.memo(function OutletImpl() {\n const router = useRouter()\n const matchId = React.useContext(matchContext)\n const routeId = useRouterState({\n select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,\n })\n\n const route = router.routesById[routeId]!\n\n const { parentGlobalNotFound } = useRouterState({\n select: (s) => {\n const matches = s.matches\n const parentMatch = matches.find((d) => d.id === matchId)\n invariant(\n parentMatch,\n `Could not find parent match for matchId \"${matchId}\"`,\n )\n return {\n parentGlobalNotFound: parentMatch.globalNotFound,\n }\n },\n })\n\n const childMatchId = useRouterState({\n select: (s) => {\n const matches = s.matches\n const index = matches.findIndex((d) => d.id === matchId)\n return matches[index + 1]?.id\n },\n })\n\n if (parentGlobalNotFound) {\n return renderRouteNotFound(router, route, undefined)\n }\n\n if (!childMatchId) {\n return null\n }\n\n const nextMatch = <Match matchId={childMatchId} />\n\n const pendingElement = router.options.defaultPendingComponent ? (\n <router.options.defaultPendingComponent />\n ) : null\n\n if (matchId === rootRouteId) {\n return (\n <React.Suspense fallback={pendingElement}>{nextMatch}</React.Suspense>\n )\n }\n\n return nextMatch\n})\n"],"names":["match"],"mappings":";;;;;;;;;;;;;;;;AAkBO;AAA4C;AAEnD;;AAGE;AACA;AAA+B;;AACd;AAAyC;AAAA;AAG1D;AAAA;AACE;AAC8C;AAG1C;AAEN;AAGA;AAEA;AAGA;AAEA;AAAqC;AAAA;AAGK;AAGpC;AAAA;AAAA;AAOA;AAEA;AAIA;AAIN;AAAgC;AACb;AAIjB;AAEI;AAAC;AAAA;AACoB;AACoB;AAGjC;AACI;AACR;AAAsB;AACxB;AAEA;AAAC;AAAA;AAKK;AAIM;AAED;AAAwD;AACjE;AAE8B;AAAA;AAChC;AAAA;AAKV;AAEO;AAAsD;AAE7D;;AAGE;AACA;AAA+B;;AACd;AAAyC;AAAA;AAGpD;AAEN;AAAkC;AAE9B;AAAkD;AACpD;AAGF;AAA6B;AAEnBA;AACN;AAAmB;AACjB;AACA;AACA;AACA;AACA;AACD;AACH;AAGI;AACJ;AACA;AAA+C;AAsBjD;AAII;AACE;AACA;AACF;AAGQ;AAAiC;AAEzC;AAAc;AAGN;AAEH;AAAwC;AAG7C;AAGF;AAMA;AAAY;AAGV;AAMF;AAEI;AAAA;AAAC;AAAA;AACc;AACP;AACY;AAClB;AAAA;AAAA;AAKF;AACF;AAEM;AAAiC;AAEvC;AAAY;AACd;AAGE;AAEF;AAGI;AAEE;AACF;AAEQ;AACN;AAAwC;AACnC;AACH;AACA;AAGJ;AACE;AAGA;AAAwC;AACnC;AACgB;AACnB;AACW;AACjB;AAGF;AAAY;AAGd;AAEK;AAAA;AAGG;AAGV;AAEO;AACL;AACM;AACN;AAA+B;;AACd;AAAyC;AAAA;AAGpD;AAEA;AAA0C;AAE5C;AACA;AACA;AAAA;AACE;AACmD;AAE9C;AAAA;AAC6B;AAAA;AAEtC;AAGF;AAAoC;;AAEhC;AACA;AACO;AAAoB;AAC7B;AAGF;AACS;AAA4C;AAGrD;AACS;AAAA;AAGT;AAEM;AAIN;AACE;AACuD;AAIlD;AACT;;;;;;"}
|
|
1
|
+
{"version":3,"file":"Match.js","sources":["../../src/Match.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport invariant from 'tiny-invariant'\nimport warning from 'tiny-warning'\nimport { CatchBoundary, ErrorComponent } from './CatchBoundary'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\nimport { createControlledPromise, pick } from './utils'\nimport { CatchNotFound, isNotFound } from './not-found'\nimport { isRedirect } from './redirects'\nimport { matchContext } from './matchContext'\nimport { defaultDeserializeError, isServerSideError } from './isServerSideError'\nimport { SafeFragment } from './SafeFragment'\nimport { renderRouteNotFound } from './renderRouteNotFound'\nimport { rootRouteId } from './root'\nimport type { AnyRoute } from './route'\n\nexport const Match = React.memo(function MatchImpl({\n matchId,\n}: {\n matchId: string\n}) {\n const router = useRouter()\n const routeId = useRouterState({\n select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,\n })\n\n invariant(\n routeId,\n `Could not find routeId for matchId \"${matchId}\". Please file an issue!`,\n )\n\n const route: AnyRoute = router.routesById[routeId]\n\n const PendingComponent =\n route.options.pendingComponent ?? router.options.defaultPendingComponent\n\n const pendingElement = PendingComponent ? <PendingComponent /> : null\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 globalNotFound 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 ResolvedSuspenseBoundary =\n // If we're on the root route, allow forcefully wrapping in suspense\n (!route.isRoot || route.options.wrapInSuspense) &&\n (route.options.wrapInSuspense ??\n PendingComponent ??\n (route.options.errorComponent as any)?.preload)\n ? React.Suspense\n : SafeFragment\n\n const ResolvedCatchBoundary = routeErrorComponent\n ? CatchBoundary\n : SafeFragment\n\n const ResolvedNotFoundBoundary = routeNotFoundComponent\n ? CatchNotFound\n : SafeFragment\n\n const resetKey = useRouterState({\n select: (s) => s.loadedAt,\n })\n\n return (\n <matchContext.Provider value={matchId}>\n <ResolvedSuspenseBoundary fallback={pendingElement}>\n <ResolvedCatchBoundary\n getResetKey={() => resetKey}\n errorComponent={routeErrorComponent || ErrorComponent}\n onCatch={(error, errorInfo) => {\n // Forward not found errors (we don't want to show the error component for these)\n if (isNotFound(error)) throw error\n warning(false, `Error in route match: ${matchId}`)\n routeOnCatch?.(error, errorInfo)\n }}\n >\n <ResolvedNotFoundBoundary\n fallback={(error) => {\n // If the current not found handler doesn't exist or it has a\n // route ID which doesn't match the current route, rethrow the error\n if (\n !routeNotFoundComponent ||\n (error.routeId && error.routeId !== routeId) ||\n (!error.routeId && !route.isRoot)\n )\n throw error\n\n return React.createElement(routeNotFoundComponent, error as any)\n }}\n >\n <MatchInner matchId={matchId} />\n </ResolvedNotFoundBoundary>\n </ResolvedCatchBoundary>\n </ResolvedSuspenseBoundary>\n </matchContext.Provider>\n )\n})\n\nexport const MatchInner = React.memo(function MatchInnerImpl({\n matchId,\n}: {\n matchId: string\n}): any {\n const router = useRouter()\n const routeId = useRouterState({\n select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,\n })\n\n const route = router.routesById[routeId]!\n\n const matchIndex = useRouterState({\n select: (s) => {\n return s.matches.findIndex((d) => d.id === matchId)\n },\n })\n\n const match = useRouterState({\n select: (s) => {\n const match = s.matches[matchIndex]!\n return pick(match, [\n 'id',\n 'status',\n 'error',\n 'loadPromise',\n 'minPendingPromise',\n ])\n },\n })\n\n const out = React.useMemo(() => {\n const Comp = route.options.component ?? router.options.defaultComponent\n return Comp ? <Comp key={matchId} /> : <Outlet />\n }, [matchId, route.options.component, router.options.defaultComponent])\n\n React.useEffect(() => {\n if (match.status === 'pending') {\n // We're pending, and if we have a minPendingMs, we need to wait for it\n const pendingMinMs =\n route.options.pendingMinMs ?? router.options.defaultPendingMinMs\n\n if (pendingMinMs && !match.minPendingPromise) {\n // Create a promise that will resolve after the minPendingMs\n if (!router.isServer) {\n const minPendingPromise = createControlledPromise<void>()\n\n router.updateMatch(match.id, (prev) => ({\n ...prev,\n minPendingPromise,\n }))\n\n const id = setTimeout(() => {\n minPendingPromise.resolve()\n\n // We've handled the minPendingPromise, so we can delete it\n router.updateMatch(match.id, (prev) => ({\n ...prev,\n minPendingPromise: undefined,\n }))\n }, pendingMinMs)\n return () => clearTimeout(id)\n }\n }\n }\n return undefined\n }, [\n match.id,\n match.loadPromise,\n match.minPendingPromise,\n match.status,\n route.options.pendingMinMs,\n router,\n ])\n\n // function useChangedDiff(value: any) {\n // const ref = React.useRef(value)\n // const changed = ref.current !== value\n // if (changed) {\n // console.log(\n // 'Changed:',\n // value,\n // Object.fromEntries(\n // Object.entries(value).filter(\n // ([key, val]) => val !== ref.current[key],\n // ),\n // ),\n // )\n // }\n // ref.current = value\n // }\n\n // useChangedDiff(match)\n\n const RouteErrorComponent =\n (route.options.errorComponent ?? router.options.defaultErrorComponent) ||\n ErrorComponent\n\n if (match.status === 'notFound') {\n let error: unknown\n if (isServerSideError(match.error)) {\n const deserializeError =\n router.options.errorSerializer?.deserialize ?? defaultDeserializeError\n\n error = deserializeError(match.error.data)\n } else {\n error = match.error\n }\n\n invariant(isNotFound(error), 'Expected a notFound error')\n\n return renderRouteNotFound(router, route, error)\n }\n\n if (match.status === 'redirected') {\n // Redirects should be handled by the router transition. If we happen to\n // encounter a redirect here, it's a bug. Let's warn, but render nothing.\n invariant(isRedirect(match.error), 'Expected a redirect error')\n\n // warning(\n // false,\n // 'Tried to render a redirected route match! This is a weird circumstance, please file an issue!',\n // )\n throw match.loadPromise\n }\n\n if (match.status === 'error') {\n // If we're on the server, we need to use React's new and super\n // wonky api for throwing errors from a server side render inside\n // of a suspense boundary. This is the only way to get\n // renderToPipeableStream to not hang indefinitely.\n // We'll serialize the error and rethrow it on the client.\n if (router.isServer) {\n return (\n <RouteErrorComponent\n error={match.error}\n info={{\n componentStack: '',\n }}\n />\n )\n }\n\n if (isServerSideError(match.error)) {\n const deserializeError =\n router.options.errorSerializer?.deserialize ?? defaultDeserializeError\n throw deserializeError(match.error.data)\n } else {\n throw match.error\n }\n }\n\n if (match.status === 'pending') {\n throw match.loadPromise\n }\n\n return (\n <>\n {out}\n {router.AfterEachMatch ? (\n <router.AfterEachMatch match={match} matchIndex={matchIndex} />\n ) : null}\n </>\n )\n})\n\nexport const Outlet = React.memo(function OutletImpl() {\n const router = useRouter()\n const matchId = React.useContext(matchContext)\n const routeId = useRouterState({\n select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,\n })\n\n const route = router.routesById[routeId]!\n\n const { parentGlobalNotFound } = useRouterState({\n select: (s) => {\n const matches = s.matches\n const parentMatch = matches.find((d) => d.id === matchId)\n invariant(\n parentMatch,\n `Could not find parent match for matchId \"${matchId}\"`,\n )\n return {\n parentGlobalNotFound: parentMatch.globalNotFound,\n }\n },\n })\n\n const childMatchId = useRouterState({\n select: (s) => {\n const matches = s.matches\n const index = matches.findIndex((d) => d.id === matchId)\n return matches[index + 1]?.id\n },\n })\n\n if (parentGlobalNotFound) {\n return renderRouteNotFound(router, route, undefined)\n }\n\n if (!childMatchId) {\n return null\n }\n\n const nextMatch = <Match matchId={childMatchId} />\n\n const pendingElement = router.options.defaultPendingComponent ? (\n <router.options.defaultPendingComponent />\n ) : null\n\n if (matchId === rootRouteId) {\n return (\n <React.Suspense fallback={pendingElement}>{nextMatch}</React.Suspense>\n )\n }\n\n return nextMatch\n})\n"],"names":["match"],"mappings":";;;;;;;;;;;;;;;;AAkBO;AAA4C;AAEnD;;AAGE;AACA;AAA+B;;AACd;AAAyC;AAAA;AAG1D;AAAA;AACE;AAC8C;AAG1C;AAEN;AAGA;AAEA;AAGA;AAEA;AAAqC;AAAA;AAGK;AAGpC;AAAA;AAAA;AAOA;AAEA;AAIA;AAIN;AAAgC;AACb;AAIjB;AAEI;AAAC;AAAA;AACoB;AACoB;AAGjC;AACI;AACR;AAAsB;AACxB;AAEA;AAAC;AAAA;AAKK;AAIM;AAED;AAAwD;AACjE;AAE8B;AAAA;AAChC;AAAA;AAKV;AAEO;AAAsD;AAE7D;;AAGE;AACA;AAA+B;;AACd;AAAyC;AAAA;AAGpD;AAEN;AAAkC;AAE9B;AAAkD;AACpD;AAGF;AAA6B;AAEnBA;AACN;AAAmB;AACjB;AACA;AACA;AACA;AACA;AACD;AACH;AAGI;AACJ;AACA;AAA+C;AAGjD;AACM;AAEF;AAGI;AAEE;AACF;AAEA;AAAwC;AACnC;AACH;AAGI;AACJ;AAGA;AAAwC;AACnC;AACgB;AACnB;AAEG;AAAqB;AAC9B;AACF;AAEK;AAAA;AACN;AACK;AACA;AACA;AACA;AACQ;AACd;AAsBF;AAII;AACE;AACA;AACF;AAGQ;AAAiC;AAEzC;AAAc;AAGN;AAEH;AAAwC;AAG7C;AAGF;AAMA;AAAY;AAGV;AAMF;AAEI;AAAA;AAAC;AAAA;AACc;AACP;AACY;AAClB;AAAA;AAAA;AAKF;AACF;AAEM;AAAiC;AAEvC;AAAY;AACd;AAGE;AACF;AAAY;AAGd;AAEK;AAAA;AAGG;AAGV;AAEO;AACL;AACM;AACN;AAA+B;;AACd;AAAyC;AAAA;AAGpD;AAEA;AAA0C;AAE5C;AACA;AACA;AAAA;AACE;AACmD;AAE9C;AAAA;AAC6B;AAAA;AAEtC;AAGF;AAAoC;;AAEhC;AACA;AACO;AAAoB;AAC7B;AAGF;AACS;AAA4C;AAGrD;AACS;AAAA;AAGT;AAEM;AAIN;AACE;AACuD;AAIlD;AACT;;;;;;"}
|
package/package.json
CHANGED
package/src/Match.tsx
CHANGED
|
@@ -141,6 +141,45 @@ export const MatchInner = React.memo(function MatchInnerImpl({
|
|
|
141
141
|
return Comp ? <Comp key={matchId} /> : <Outlet />
|
|
142
142
|
}, [matchId, route.options.component, router.options.defaultComponent])
|
|
143
143
|
|
|
144
|
+
React.useEffect(() => {
|
|
145
|
+
if (match.status === 'pending') {
|
|
146
|
+
// We're pending, and if we have a minPendingMs, we need to wait for it
|
|
147
|
+
const pendingMinMs =
|
|
148
|
+
route.options.pendingMinMs ?? router.options.defaultPendingMinMs
|
|
149
|
+
|
|
150
|
+
if (pendingMinMs && !match.minPendingPromise) {
|
|
151
|
+
// Create a promise that will resolve after the minPendingMs
|
|
152
|
+
if (!router.isServer) {
|
|
153
|
+
const minPendingPromise = createControlledPromise<void>()
|
|
154
|
+
|
|
155
|
+
router.updateMatch(match.id, (prev) => ({
|
|
156
|
+
...prev,
|
|
157
|
+
minPendingPromise,
|
|
158
|
+
}))
|
|
159
|
+
|
|
160
|
+
const id = setTimeout(() => {
|
|
161
|
+
minPendingPromise.resolve()
|
|
162
|
+
|
|
163
|
+
// We've handled the minPendingPromise, so we can delete it
|
|
164
|
+
router.updateMatch(match.id, (prev) => ({
|
|
165
|
+
...prev,
|
|
166
|
+
minPendingPromise: undefined,
|
|
167
|
+
}))
|
|
168
|
+
}, pendingMinMs)
|
|
169
|
+
return () => clearTimeout(id)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return undefined
|
|
174
|
+
}, [
|
|
175
|
+
match.id,
|
|
176
|
+
match.loadPromise,
|
|
177
|
+
match.minPendingPromise,
|
|
178
|
+
match.status,
|
|
179
|
+
route.options.pendingMinMs,
|
|
180
|
+
router,
|
|
181
|
+
])
|
|
182
|
+
|
|
144
183
|
// function useChangedDiff(value: any) {
|
|
145
184
|
// const ref = React.useRef(value)
|
|
146
185
|
// const changed = ref.current !== value
|
|
@@ -219,34 +258,6 @@ export const MatchInner = React.memo(function MatchInnerImpl({
|
|
|
219
258
|
}
|
|
220
259
|
|
|
221
260
|
if (match.status === 'pending') {
|
|
222
|
-
// We're pending, and if we have a minPendingMs, we need to wait for it
|
|
223
|
-
const pendingMinMs =
|
|
224
|
-
route.options.pendingMinMs ?? router.options.defaultPendingMinMs
|
|
225
|
-
|
|
226
|
-
if (pendingMinMs && !match.minPendingPromise) {
|
|
227
|
-
// Create a promise that will resolve after the minPendingMs
|
|
228
|
-
if (!router.isServer) {
|
|
229
|
-
const minPendingPromise = createControlledPromise<void>()
|
|
230
|
-
|
|
231
|
-
Promise.resolve().then(() => {
|
|
232
|
-
router.updateMatch(match.id, (prev) => ({
|
|
233
|
-
...prev,
|
|
234
|
-
minPendingPromise,
|
|
235
|
-
}))
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
setTimeout(() => {
|
|
239
|
-
minPendingPromise.resolve()
|
|
240
|
-
|
|
241
|
-
// We've handled the minPendingPromise, so we can delete it
|
|
242
|
-
router.updateMatch(match.id, (prev) => ({
|
|
243
|
-
...prev,
|
|
244
|
-
minPendingPromise: undefined,
|
|
245
|
-
}))
|
|
246
|
-
}, pendingMinMs)
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
261
|
throw match.loadPromise
|
|
251
262
|
}
|
|
252
263
|
|