@tanstack/react-router
Version:
Modern and scalable routing for React applications
238 lines (237 loc) • 9.04 kB
JavaScript
import { jsxs, Fragment, jsx } from "react/jsx-runtime";
import * as React from "react";
import invariant from "tiny-invariant";
import warning from "tiny-warning";
import { isNotFound, rootRouteId, pick, isRedirect, createControlledPromise, getLocationChangeInfo } from "@tanstack/router-core";
import { CatchBoundary, ErrorComponent } from "./CatchBoundary.js";
import { useRouterState } from "./useRouterState.js";
import { useRouter } from "./useRouter.js";
import { CatchNotFound } from "./not-found.js";
import { matchContext } from "./matchContext.js";
import { SafeFragment } from "./SafeFragment.js";
import { renderRouteNotFound } from "./renderRouteNotFound.js";
import { ScrollRestoration } from "./scroll-restoration.js";
const Match = React.memo(function MatchImpl({
matchId
}) {
var _a, _b;
const router = useRouter();
const routeId = useRouterState({
select: (s) => {
var _a2;
return (_a2 = s.matches.find((d) => d.id === matchId)) == null ? void 0 : _a2.routeId;
}
});
invariant(
routeId,
`Could not find routeId for matchId "${matchId}". Please file an issue!`
);
const route = router.routesById[routeId];
const PendingComponent = route.options.pendingComponent ?? router.options.defaultPendingComponent;
const pendingElement = PendingComponent ? /* @__PURE__ */ jsx(PendingComponent, {}) : null;
const routeErrorComponent = route.options.errorComponent ?? router.options.defaultErrorComponent;
const routeOnCatch = route.options.onCatch ?? router.options.defaultOnCatch;
const routeNotFoundComponent = route.isRoot ? (
// If it's the root route, use the globalNotFound option, with fallback to the notFoundRoute's component
route.options.notFoundComponent ?? ((_a = router.options.notFoundRoute) == null ? void 0 : _a.options.component)
) : route.options.notFoundComponent;
const ResolvedSuspenseBoundary = (
// If we're on the root route, allow forcefully wrapping in suspense
(!route.isRoot || route.options.wrapInSuspense) && (route.options.wrapInSuspense ?? PendingComponent ?? ((_b = route.options.errorComponent) == null ? void 0 : _b.preload)) ? React.Suspense : SafeFragment
);
const ResolvedCatchBoundary = routeErrorComponent ? CatchBoundary : SafeFragment;
const ResolvedNotFoundBoundary = routeNotFoundComponent ? CatchNotFound : SafeFragment;
const resetKey = useRouterState({
select: (s) => s.loadedAt
});
const parentRouteId = useRouterState({
select: (s) => {
var _a2;
const index = s.matches.findIndex((d) => d.id === matchId);
return (_a2 = s.matches[index - 1]) == null ? void 0 : _a2.routeId;
}
});
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(matchContext.Provider, { value: matchId, children: /* @__PURE__ */ jsx(ResolvedSuspenseBoundary, { fallback: pendingElement, children: /* @__PURE__ */ jsx(
ResolvedCatchBoundary,
{
getResetKey: () => resetKey,
errorComponent: routeErrorComponent || ErrorComponent,
onCatch: (error, errorInfo) => {
if (isNotFound(error)) throw error;
warning(false, `Error in route match: ${matchId}`);
routeOnCatch == null ? void 0 : routeOnCatch(error, errorInfo);
},
children: /* @__PURE__ */ jsx(
ResolvedNotFoundBoundary,
{
fallback: (error) => {
if (!routeNotFoundComponent || error.routeId && error.routeId !== routeId || !error.routeId && !route.isRoot)
throw error;
return React.createElement(routeNotFoundComponent, error);
},
children: /* @__PURE__ */ jsx(MatchInner, { matchId })
}
)
}
) }) }),
parentRouteId === rootRouteId && router.options.scrollRestoration ? /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(OnRendered, {}),
/* @__PURE__ */ jsx(ScrollRestoration, {})
] }) : null
] });
});
function OnRendered() {
var _a;
const router = useRouter();
const prevLocationRef = React.useRef(
void 0
);
return /* @__PURE__ */ jsx(
"script",
{
suppressHydrationWarning: true,
ref: (el) => {
var _a2;
if (el && (prevLocationRef.current === void 0 || prevLocationRef.current.href !== ((_a2 = router.state.resolvedLocation) == null ? void 0 : _a2.href))) {
router.emit({
type: "onRendered",
...getLocationChangeInfo(router.state)
});
prevLocationRef.current = router.state.resolvedLocation;
}
}
},
(_a = router.state.resolvedLocation) == null ? void 0 : _a.state.key
);
}
const MatchInner = React.memo(function MatchInnerImpl({
matchId
}) {
var _a, _b, _c;
const router = useRouter();
const { match, key, routeId } = useRouterState({
select: (s) => {
const matchIndex = s.matches.findIndex((d) => d.id === matchId);
const match2 = s.matches[matchIndex];
const routeId2 = match2.routeId;
const remountFn = router.routesById[routeId2].options.remountDeps ?? router.options.defaultRemountDeps;
const remountDeps = remountFn == null ? void 0 : remountFn({
routeId: routeId2,
loaderDeps: match2.loaderDeps,
params: match2._strictParams,
search: match2._strictSearch
});
const key2 = remountDeps ? JSON.stringify(remountDeps) : void 0;
return {
key: key2,
routeId: routeId2,
match: pick(match2, ["id", "status", "error"])
};
},
structuralSharing: true
});
const route = router.routesById[routeId];
const out = React.useMemo(() => {
const Comp = route.options.component ?? router.options.defaultComponent;
if (Comp) {
return /* @__PURE__ */ jsx(Comp, {}, key);
}
return /* @__PURE__ */ jsx(Outlet, {});
}, [key, route.options.component, router.options.defaultComponent]);
const RouteErrorComponent = (route.options.errorComponent ?? router.options.defaultErrorComponent) || ErrorComponent;
if (match.status === "notFound") {
invariant(isNotFound(match.error), "Expected a notFound error");
return renderRouteNotFound(router, route, match.error);
}
if (match.status === "redirected") {
invariant(isRedirect(match.error), "Expected a redirect error");
throw (_a = router.getMatch(match.id)) == null ? void 0 : _a.loadPromise;
}
if (match.status === "error") {
if (router.isServer) {
return /* @__PURE__ */ jsx(
RouteErrorComponent,
{
error: match.error,
reset: void 0,
info: {
componentStack: ""
}
}
);
}
throw match.error;
}
if (match.status === "pending") {
const pendingMinMs = route.options.pendingMinMs ?? router.options.defaultPendingMinMs;
if (pendingMinMs && !((_b = router.getMatch(match.id)) == null ? void 0 : _b.minPendingPromise)) {
if (!router.isServer) {
const minPendingPromise = createControlledPromise();
Promise.resolve().then(() => {
router.updateMatch(match.id, (prev) => ({
...prev,
minPendingPromise
}));
});
setTimeout(() => {
minPendingPromise.resolve();
router.updateMatch(match.id, (prev) => ({
...prev,
minPendingPromise: void 0
}));
}, pendingMinMs);
}
}
throw (_c = router.getMatch(match.id)) == null ? void 0 : _c.loadPromise;
}
return out;
});
const Outlet = React.memo(function OutletImpl() {
const router = useRouter();
const matchId = React.useContext(matchContext);
const routeId = useRouterState({
select: (s) => {
var _a;
return (_a = s.matches.find((d) => d.id === matchId)) == null ? void 0 : _a.routeId;
}
});
const route = router.routesById[routeId];
const parentGlobalNotFound = useRouterState({
select: (s) => {
const matches = s.matches;
const parentMatch = matches.find((d) => d.id === matchId);
invariant(
parentMatch,
`Could not find parent match for matchId "${matchId}"`
);
return parentMatch.globalNotFound;
}
});
const childMatchId = useRouterState({
select: (s) => {
var _a;
const matches = s.matches;
const index = matches.findIndex((d) => d.id === matchId);
return (_a = matches[index + 1]) == null ? void 0 : _a.id;
}
});
if (parentGlobalNotFound) {
return renderRouteNotFound(router, route, void 0);
}
if (!childMatchId) {
return null;
}
const nextMatch = /* @__PURE__ */ jsx(Match, { matchId: childMatchId });
const pendingElement = router.options.defaultPendingComponent ? /* @__PURE__ */ jsx(router.options.defaultPendingComponent, {}) : null;
if (matchId === rootRouteId) {
return /* @__PURE__ */ jsx(React.Suspense, { fallback: pendingElement, children: nextMatch });
}
return nextMatch;
});
export {
Match,
MatchInner,
Outlet
};
//# sourceMappingURL=Match.js.map