@tanstack/react-router 1.32.16 → 1.33.0
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/CatchBoundary.cjs +2 -4
- package/dist/cjs/CatchBoundary.cjs.map +1 -1
- package/dist/cjs/CatchBoundary.d.cts +5 -3
- package/dist/cjs/Matches.cjs +13 -41
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/Matches.d.cts +2 -11
- package/dist/cjs/fileRoute.cjs +7 -5
- package/dist/cjs/fileRoute.cjs.map +1 -1
- package/dist/cjs/fileRoute.d.cts +1 -0
- package/dist/cjs/index.cjs +8 -4
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +6 -2
- package/dist/cjs/link.cjs +2 -2
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/not-found.cjs +2 -2
- package/dist/cjs/not-found.cjs.map +1 -1
- package/dist/cjs/not-found.d.cts +2 -1
- package/dist/cjs/root.cjs +5 -0
- package/dist/cjs/root.cjs.map +1 -0
- package/dist/cjs/root.d.cts +2 -0
- package/dist/cjs/route.cjs +19 -18
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +3 -3
- package/dist/cjs/router.cjs +56 -52
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +2 -0
- package/dist/cjs/useLoaderData.cjs +13 -0
- package/dist/cjs/useLoaderData.cjs.map +1 -0
- package/dist/cjs/useLoaderData.d.cts +9 -0
- package/dist/cjs/useLoaderDeps.cjs +13 -0
- package/dist/cjs/useLoaderDeps.cjs.map +1 -0
- package/dist/cjs/useLoaderDeps.d.cts +9 -0
- package/dist/cjs/useMatch.cjs +41 -0
- package/dist/cjs/useMatch.cjs.map +1 -0
- package/dist/cjs/useMatch.d.cts +9 -0
- package/dist/cjs/useNavigate.cjs +2 -2
- package/dist/cjs/useNavigate.cjs.map +1 -1
- package/dist/cjs/useParams.cjs +2 -2
- package/dist/cjs/useParams.cjs.map +1 -1
- package/dist/cjs/useRouteContext.cjs +2 -2
- package/dist/cjs/useRouteContext.cjs.map +1 -1
- package/dist/cjs/useSearch.cjs +2 -2
- package/dist/cjs/useSearch.cjs.map +1 -1
- package/dist/esm/CatchBoundary.d.ts +5 -3
- package/dist/esm/CatchBoundary.js +2 -4
- package/dist/esm/CatchBoundary.js.map +1 -1
- package/dist/esm/Matches.d.ts +2 -11
- package/dist/esm/Matches.js +13 -41
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/fileRoute.d.ts +1 -0
- package/dist/esm/fileRoute.js +3 -1
- package/dist/esm/fileRoute.js.map +1 -1
- package/dist/esm/index.d.ts +6 -2
- package/dist/esm/index.js +6 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/link.js +1 -1
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/not-found.d.ts +2 -1
- package/dist/esm/not-found.js +2 -2
- package/dist/esm/not-found.js.map +1 -1
- package/dist/esm/root.d.ts +2 -0
- package/dist/esm/root.js +5 -0
- package/dist/esm/root.js.map +1 -0
- package/dist/esm/route.d.ts +3 -3
- package/dist/esm/route.js +4 -3
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +2 -0
- package/dist/esm/router.js +7 -3
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/useLoaderData.d.ts +9 -0
- package/dist/esm/useLoaderData.js +13 -0
- package/dist/esm/useLoaderData.js.map +1 -0
- package/dist/esm/useLoaderDeps.d.ts +9 -0
- package/dist/esm/useLoaderDeps.js +13 -0
- package/dist/esm/useLoaderDeps.js.map +1 -0
- package/dist/esm/useMatch.d.ts +9 -0
- package/dist/esm/useMatch.js +24 -0
- package/dist/esm/useMatch.js.map +1 -0
- package/dist/esm/useNavigate.js +1 -1
- package/dist/esm/useNavigate.js.map +1 -1
- package/dist/esm/useParams.js +1 -1
- package/dist/esm/useParams.js.map +1 -1
- package/dist/esm/useRouteContext.js +1 -1
- package/dist/esm/useRouteContext.js.map +1 -1
- package/dist/esm/useSearch.js +1 -1
- package/dist/esm/useSearch.js.map +1 -1
- package/package.json +1 -1
- package/src/CatchBoundary.tsx +14 -11
- package/src/Matches.tsx +27 -93
- package/src/fileRoute.ts +4 -2
- package/src/index.tsx +4 -5
- package/src/link.tsx +1 -1
- package/src/not-found.tsx +4 -3
- package/src/root.ts +2 -0
- package/src/route.ts +7 -4
- package/src/router.ts +9 -4
- package/src/useLoaderData.tsx +29 -0
- package/src/useLoaderDeps.tsx +29 -0
- package/src/useMatch.tsx +40 -0
- package/src/useNavigate.tsx +1 -1
- package/src/useParams.tsx +1 -1
- package/src/useRouteContext.ts +1 -1
- package/src/useSearch.tsx +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNavigate.js","sources":["../../src/useNavigate.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useMatch } from './
|
|
1
|
+
{"version":3,"file":"useNavigate.js","sources":["../../src/useNavigate.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useMatch } from './useMatch'\nimport { useRouter } from './useRouter'\n\nimport type { FromPathOption, NavigateOptions } from './link'\nimport type { RoutePaths } from './routeInfo'\nimport type { AnyRouter, RegisteredRouter } from './router'\n\nexport type UseNavigateResult<TDefaultFrom extends string> = <\n TTo extends string,\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = TDefaultFrom,\n TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,\n TMaskTo extends string = '',\n>({\n from,\n ...rest\n}: NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>) => Promise<void>\n\nexport function useNavigate<\n TRouter extends AnyRouter = RegisteredRouter,\n TDefaultFrom extends string = string,\n>(_defaultOpts?: {\n from?: FromPathOption<TRouter, TDefaultFrom>\n}): UseNavigateResult<TDefaultFrom> {\n const router = useRouter()\n\n return React.useCallback(\n (options: NavigateOptions) => {\n return router.navigate({\n ...options,\n from: options.to ? router.state.resolvedLocation.pathname : undefined,\n })\n },\n [router],\n ) as UseNavigateResult<TDefaultFrom>\n}\n\n// NOTE: I don't know of anyone using this. It's undocumented, so let's wait until someone needs it\n// export function typedNavigate<\n// TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n// TDefaultFrom extends RoutePaths<TRouteTree> = '/',\n// >(navigate: (opts: NavigateOptions<any>) => Promise<void>) {\n// return navigate as <\n// TFrom extends RoutePaths<TRouteTree> = TDefaultFrom,\n// TTo extends string = '',\n// TMaskFrom extends RoutePaths<TRouteTree> = '/',\n// TMaskTo extends string = '',\n// >(\n// opts?: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,\n// ) => Promise<void>\n// } //\n\nexport function Navigate<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,\n TMaskTo extends string = '',\n>(props: NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>): null {\n const { navigate } = useRouter()\n const match = useMatch({ strict: false })\n\n React.useEffect(() => {\n navigate({\n from: props.to ? match.pathname : undefined,\n ...props,\n } as any)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n return null\n}\n"],"names":[],"mappings":";;;AAmBO,SAAS,YAGd,cAEkC;AAClC,QAAM,SAAS;AAEf,SAAO,MAAM;AAAA,IACX,CAAC,YAA6B;AAC5B,aAAO,OAAO,SAAS;AAAA,QACrB,GAAG;AAAA,QACH,MAAM,QAAQ,KAAK,OAAO,MAAM,iBAAiB,WAAW;AAAA,MAAA,CAC7D;AAAA,IACH;AAAA,IACA,CAAC,MAAM;AAAA,EAAA;AAEX;AAiBO,SAAS,SAMd,OAAuE;AACjE,QAAA,EAAE,aAAa;AACrB,QAAM,QAAQ,SAAS,EAAE,QAAQ,MAAO,CAAA;AAExC,QAAM,UAAU,MAAM;AACX,aAAA;AAAA,MACP,MAAM,MAAM,KAAK,MAAM,WAAW;AAAA,MAClC,GAAG;AAAA,IAAA,CACG;AAAA,EAEV,GAAG,CAAE,CAAA;AAEE,SAAA;AACT;"}
|
package/dist/esm/useParams.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useParams.js","sources":["../../src/useParams.tsx"],"sourcesContent":["import { useMatch } from './
|
|
1
|
+
{"version":3,"file":"useParams.js","sources":["../../src/useParams.tsx"],"sourcesContent":["import { useMatch } from './useMatch'\nimport type { AnyRoute } from './route'\nimport type { AllParams, RouteById, RouteIds } from './routeInfo'\nimport type { RegisteredRouter } from './router'\nimport type { Expand } from './utils'\nimport type { StrictOrFrom } from './utils'\n\nexport function useParams<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,\n TReturnIntersection extends boolean = false,\n TParams = TReturnIntersection extends false\n ? RouteById<TRouteTree, TFrom>['types']['allParams']\n : Expand<Partial<AllParams<TRouteTree>>>,\n TSelected = TParams,\n>(\n opts: StrictOrFrom<TFrom, TReturnIntersection> & {\n select?: (params: TParams) => TSelected\n },\n): TSelected {\n return useMatch({\n ...opts,\n select: (match) => {\n return opts.select ? opts.select(match.params as TParams) : match.params\n },\n }) as TSelected\n}\n"],"names":[],"mappings":";AAOO,SAAS,UASd,MAGW;AACX,SAAO,SAAS;AAAA,IACd,GAAG;AAAA,IACH,QAAQ,CAAC,UAAU;AACjB,aAAO,KAAK,SAAS,KAAK,OAAO,MAAM,MAAiB,IAAI,MAAM;AAAA,IACpE;AAAA,EAAA,CACD;AACH;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useRouteContext.js","sources":["../../src/useRouteContext.ts"],"sourcesContent":["import { useMatch } from './
|
|
1
|
+
{"version":3,"file":"useRouteContext.js","sources":["../../src/useRouteContext.ts"],"sourcesContent":["import { useMatch } from './useMatch'\nimport type { MakeRouteMatch } from './Matches'\nimport type { AnyRoute } from './route'\nimport type { RouteById, RouteIds } from './routeInfo'\nimport type { RegisteredRouter } from './router'\nimport type { StrictOrFrom } from './utils'\n\nexport function useRouteContext<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,\n TRouteContext = RouteById<TRouteTree, TFrom>['types']['allContext'],\n TSelected = TRouteContext,\n>(\n opts: StrictOrFrom<TFrom> & {\n select?: (search: TRouteContext) => TSelected\n },\n): TSelected {\n return useMatch({\n ...(opts as any),\n select: (match: MakeRouteMatch<TRouteTree, TFrom>) =>\n opts.select ? opts.select(match.context) : match.context,\n })\n}\n"],"names":[],"mappings":";AAOO,SAAS,gBAMd,MAGW;AACX,SAAO,SAAS;AAAA,IACd,GAAI;AAAA,IACJ,QAAQ,CAAC,UACP,KAAK,SAAS,KAAK,OAAO,MAAM,OAAO,IAAI,MAAM;AAAA,EAAA,CACpD;AACH;"}
|
package/dist/esm/useSearch.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSearch.js","sources":["../../src/useSearch.tsx"],"sourcesContent":["import { useMatch } from './
|
|
1
|
+
{"version":3,"file":"useSearch.js","sources":["../../src/useSearch.tsx"],"sourcesContent":["import { useMatch } from './useMatch'\nimport type { AnyRoute, RootSearchSchema } from './route'\nimport type { FullSearchSchema, RouteById, RouteIds } from './routeInfo'\nimport type { RegisteredRouter } from './router'\nimport type { MakeRouteMatch } from './Matches'\nimport type { StrictOrFrom } from './utils'\n\nexport function useSearch<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,\n TReturnIntersection extends boolean = false,\n TSearch = TReturnIntersection extends false\n ? Exclude<\n RouteById<TRouteTree, TFrom>['types']['fullSearchSchema'],\n RootSearchSchema\n >\n : Partial<Omit<FullSearchSchema<TRouteTree>, keyof RootSearchSchema>>,\n TSelected = TSearch,\n>(\n opts: StrictOrFrom<TFrom, TReturnIntersection> & {\n select?: (search: TSearch) => TSelected\n },\n): TSelected {\n return useMatch({\n ...opts,\n select: (match: MakeRouteMatch<TRouteTree, TFrom>) => {\n return opts.select ? opts.select(match.search) : match.search\n },\n })\n}\n"],"names":[],"mappings":";AAOO,SAAS,UAYd,MAGW;AACX,SAAO,SAAS;AAAA,IACd,GAAG;AAAA,IACH,QAAQ,CAAC,UAA6C;AACpD,aAAO,KAAK,SAAS,KAAK,OAAO,MAAM,MAAM,IAAI,MAAM;AAAA,IACzD;AAAA,EAAA,CACD;AACH;"}
|
package/package.json
CHANGED
package/src/CatchBoundary.tsx
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
|
+
import type { ErrorRouteComponent } from './route'
|
|
3
|
+
import type { ErrorInfo } from 'react'
|
|
2
4
|
|
|
3
5
|
export function CatchBoundary(props: {
|
|
4
6
|
getResetKey: () => string
|
|
5
|
-
children:
|
|
6
|
-
errorComponent?:
|
|
7
|
-
onCatch?: (error:
|
|
7
|
+
children: React.ReactNode
|
|
8
|
+
errorComponent?: ErrorRouteComponent
|
|
9
|
+
onCatch?: (error: Error, errorInfo: ErrorInfo) => void
|
|
8
10
|
}) {
|
|
9
11
|
const errorComponent = props.errorComponent ?? ErrorComponent
|
|
10
12
|
|
|
@@ -28,14 +30,17 @@ export function CatchBoundary(props: {
|
|
|
28
30
|
|
|
29
31
|
class CatchBoundaryImpl extends React.Component<{
|
|
30
32
|
getResetKey: () => string
|
|
31
|
-
children: (props: {
|
|
32
|
-
|
|
33
|
+
children: (props: {
|
|
34
|
+
error: Error | null
|
|
35
|
+
reset: () => void
|
|
36
|
+
}) => React.ReactNode
|
|
37
|
+
onCatch?: (error: Error, errorInfo: ErrorInfo) => void
|
|
33
38
|
}> {
|
|
34
|
-
state = { error: null } as { error:
|
|
39
|
+
state = { error: null } as { error: Error | null; resetKey: string }
|
|
35
40
|
static getDerivedStateFromProps(props: any) {
|
|
36
41
|
return { resetKey: props.getResetKey() }
|
|
37
42
|
}
|
|
38
|
-
static getDerivedStateFromError(error:
|
|
43
|
+
static getDerivedStateFromError(error: Error) {
|
|
39
44
|
return { error }
|
|
40
45
|
}
|
|
41
46
|
reset() {
|
|
@@ -53,11 +58,9 @@ class CatchBoundaryImpl extends React.Component<{
|
|
|
53
58
|
this.reset()
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
|
-
componentDidCatch(error:
|
|
61
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
57
62
|
if (this.props.onCatch) {
|
|
58
|
-
this.props.onCatch(error)
|
|
59
|
-
} else {
|
|
60
|
-
console.error(error)
|
|
63
|
+
this.props.onCatch(error, errorInfo)
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
render() {
|
package/src/Matches.tsx
CHANGED
|
@@ -9,8 +9,13 @@ import { CatchNotFound, DefaultGlobalNotFound, isNotFound } from './not-found'
|
|
|
9
9
|
import { isRedirect } from './redirects'
|
|
10
10
|
import { type AnyRouter, type RegisteredRouter } from './router'
|
|
11
11
|
import { Transitioner } from './Transitioner'
|
|
12
|
+
import {
|
|
13
|
+
type AnyRoute,
|
|
14
|
+
type ReactNode,
|
|
15
|
+
type StaticDataRouteOption,
|
|
16
|
+
} from './route'
|
|
17
|
+
import { rootRouteId } from './root'
|
|
12
18
|
import type { ResolveRelativePath, ToOptions } from './link'
|
|
13
|
-
import type { AnyRoute, ReactNode, StaticDataRouteOption } from './route'
|
|
14
19
|
import type {
|
|
15
20
|
AllParams,
|
|
16
21
|
FullSearchSchema,
|
|
@@ -20,13 +25,7 @@ import type {
|
|
|
20
25
|
RouteIds,
|
|
21
26
|
RoutePaths,
|
|
22
27
|
} from './routeInfo'
|
|
23
|
-
import type {
|
|
24
|
-
ControlledPromise,
|
|
25
|
-
DeepPartial,
|
|
26
|
-
Expand,
|
|
27
|
-
NoInfer,
|
|
28
|
-
StrictOrFrom,
|
|
29
|
-
} from './utils'
|
|
28
|
+
import type { ControlledPromise, DeepPartial, NoInfer } from './utils'
|
|
30
29
|
|
|
31
30
|
export const matchContext = React.createContext<string | undefined>(undefined)
|
|
32
31
|
|
|
@@ -141,9 +140,9 @@ function MatchesInner() {
|
|
|
141
140
|
onCatch={(error) => {
|
|
142
141
|
warning(
|
|
143
142
|
false,
|
|
144
|
-
`The following error wasn't caught by any route!
|
|
143
|
+
`The following error wasn't caught by any route! At the very least, consider setting an 'errorComponent' in your RootRoute!`,
|
|
145
144
|
)
|
|
146
|
-
|
|
145
|
+
warning(false, error.message || error.toString())
|
|
147
146
|
}}
|
|
148
147
|
>
|
|
149
148
|
{matchId ? <Match matchId={matchId} /> : null}
|
|
@@ -177,6 +176,8 @@ export function Match({ matchId }: { matchId: string }) {
|
|
|
177
176
|
const routeErrorComponent =
|
|
178
177
|
route.options.errorComponent ?? router.options.defaultErrorComponent
|
|
179
178
|
|
|
179
|
+
const routeOnCatch = route.options.onCatch ?? router.options.defaultOnCatch
|
|
180
|
+
|
|
180
181
|
const routeNotFoundComponent = route.isRoot
|
|
181
182
|
? // If it's the root route, use the globalNotFound option, with fallback to the notFoundRoute's component
|
|
182
183
|
route.options.notFoundComponent ??
|
|
@@ -208,12 +209,12 @@ export function Match({ matchId }: { matchId: string }) {
|
|
|
208
209
|
<ResolvedSuspenseBoundary fallback={pendingElement}>
|
|
209
210
|
<ResolvedCatchBoundary
|
|
210
211
|
getResetKey={() => resetKey}
|
|
211
|
-
errorComponent={routeErrorComponent
|
|
212
|
-
onCatch={(error) => {
|
|
212
|
+
errorComponent={routeErrorComponent || ErrorComponent}
|
|
213
|
+
onCatch={(error, errorInfo) => {
|
|
213
214
|
// Forward not found errors (we don't want to show the error component for these)
|
|
214
215
|
if (isNotFound(error)) throw error
|
|
215
216
|
warning(false, `Error in route match: ${matchId}`)
|
|
216
|
-
|
|
217
|
+
routeOnCatch?.(error, errorInfo)
|
|
217
218
|
}}
|
|
218
219
|
>
|
|
219
220
|
<ResolvedNotFoundBoundary
|
|
@@ -417,7 +418,19 @@ export const Outlet = React.memo(function Outlet() {
|
|
|
417
418
|
return null
|
|
418
419
|
}
|
|
419
420
|
|
|
420
|
-
|
|
421
|
+
const nextMatch = <Match matchId={childMatchId} />
|
|
422
|
+
|
|
423
|
+
const pendingElement = router.options.defaultPendingComponent ? (
|
|
424
|
+
<router.options.defaultPendingComponent />
|
|
425
|
+
) : null
|
|
426
|
+
|
|
427
|
+
if (matchId === rootRouteId) {
|
|
428
|
+
return (
|
|
429
|
+
<React.Suspense fallback={pendingElement}>{nextMatch}</React.Suspense>
|
|
430
|
+
)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return nextMatch
|
|
421
434
|
})
|
|
422
435
|
|
|
423
436
|
function renderRouteNotFound(router: AnyRouter, route: AnyRoute, data: any) {
|
|
@@ -532,39 +545,6 @@ export function MatchRoute<
|
|
|
532
545
|
return params ? props.children : null
|
|
533
546
|
}
|
|
534
547
|
|
|
535
|
-
export function useMatch<
|
|
536
|
-
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
537
|
-
TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
|
|
538
|
-
TReturnIntersection extends boolean = false,
|
|
539
|
-
TRouteMatch = MakeRouteMatch<TRouteTree, TFrom, TReturnIntersection>,
|
|
540
|
-
TSelected = TRouteMatch,
|
|
541
|
-
>(
|
|
542
|
-
opts: StrictOrFrom<TFrom, TReturnIntersection> & {
|
|
543
|
-
select?: (match: TRouteMatch) => TSelected
|
|
544
|
-
},
|
|
545
|
-
): TSelected {
|
|
546
|
-
const nearestMatchId = React.useContext(matchContext)
|
|
547
|
-
|
|
548
|
-
const matchSelection = useRouterState({
|
|
549
|
-
select: (state) => {
|
|
550
|
-
const match = state.matches.find((d) =>
|
|
551
|
-
opts.from ? opts.from === d.routeId : d.id === nearestMatchId,
|
|
552
|
-
)
|
|
553
|
-
|
|
554
|
-
invariant(
|
|
555
|
-
match,
|
|
556
|
-
`Could not find ${
|
|
557
|
-
opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'
|
|
558
|
-
}`,
|
|
559
|
-
)
|
|
560
|
-
|
|
561
|
-
return opts.select ? opts.select(match as any) : match
|
|
562
|
-
},
|
|
563
|
-
})
|
|
564
|
-
|
|
565
|
-
return matchSelection as TSelected
|
|
566
|
-
}
|
|
567
|
-
|
|
568
548
|
export function useMatches<
|
|
569
549
|
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
570
550
|
TRouteId extends RouteIds<TRouteTree> = ParseRoute<TRouteTree>['id'],
|
|
@@ -634,52 +614,6 @@ export function useChildMatches<
|
|
|
634
614
|
})
|
|
635
615
|
}
|
|
636
616
|
|
|
637
|
-
export function useLoaderDeps<
|
|
638
|
-
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
639
|
-
TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
|
|
640
|
-
TRouteMatch extends MakeRouteMatch<TRouteTree, TFrom> = MakeRouteMatch<
|
|
641
|
-
TRouteTree,
|
|
642
|
-
TFrom
|
|
643
|
-
>,
|
|
644
|
-
TSelected = Required<TRouteMatch>['loaderDeps'],
|
|
645
|
-
>(
|
|
646
|
-
opts: StrictOrFrom<TFrom> & {
|
|
647
|
-
select?: (match: TRouteMatch) => TSelected
|
|
648
|
-
},
|
|
649
|
-
): TSelected {
|
|
650
|
-
return useMatch({
|
|
651
|
-
...opts,
|
|
652
|
-
select: (s) => {
|
|
653
|
-
return typeof opts.select === 'function'
|
|
654
|
-
? opts.select(s.loaderDeps)
|
|
655
|
-
: s.loaderDeps
|
|
656
|
-
},
|
|
657
|
-
})
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
export function useLoaderData<
|
|
661
|
-
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
662
|
-
TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
|
|
663
|
-
TRouteMatch extends MakeRouteMatch<TRouteTree, TFrom> = MakeRouteMatch<
|
|
664
|
-
TRouteTree,
|
|
665
|
-
TFrom
|
|
666
|
-
>,
|
|
667
|
-
TSelected = Required<TRouteMatch>['loaderData'],
|
|
668
|
-
>(
|
|
669
|
-
opts: StrictOrFrom<TFrom> & {
|
|
670
|
-
select?: (match: TRouteMatch) => TSelected
|
|
671
|
-
},
|
|
672
|
-
): TSelected {
|
|
673
|
-
return useMatch({
|
|
674
|
-
...opts,
|
|
675
|
-
select: (s) => {
|
|
676
|
-
return typeof opts.select === 'function'
|
|
677
|
-
? opts.select(s.loaderData as TRouteMatch)
|
|
678
|
-
: s.loaderData
|
|
679
|
-
},
|
|
680
|
-
}) as TSelected
|
|
681
|
-
}
|
|
682
|
-
|
|
683
617
|
export function isServerSideError(error: unknown): error is {
|
|
684
618
|
__isServerError: true
|
|
685
619
|
data: Record<string, any>
|
package/src/fileRoute.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import warning from 'tiny-warning'
|
|
2
2
|
import { createRoute } from './route'
|
|
3
|
-
import {
|
|
3
|
+
import { useMatch } from './useMatch'
|
|
4
|
+
import { useLoaderDeps } from './useLoaderDeps'
|
|
5
|
+
import { useLoaderData } from './useLoaderData'
|
|
4
6
|
import { useSearch } from './useSearch'
|
|
5
7
|
import { useParams } from './useParams'
|
|
6
8
|
import { useNavigate } from './useNavigate'
|
|
@@ -18,7 +20,6 @@ import type {
|
|
|
18
20
|
ResolveLoaderData,
|
|
19
21
|
ResolveRouteContext,
|
|
20
22
|
ResolveSearchSchemaUsed,
|
|
21
|
-
RootRouteId,
|
|
22
23
|
Route,
|
|
23
24
|
RouteConstraints,
|
|
24
25
|
RouteContext,
|
|
@@ -32,6 +33,7 @@ import type { MakeRouteMatch } from './Matches'
|
|
|
32
33
|
import type { NoInfer } from '@tanstack/react-store'
|
|
33
34
|
import type { RegisteredRouter } from './router'
|
|
34
35
|
import type { RouteById, RouteIds } from './routeInfo'
|
|
36
|
+
import type { RootRouteId } from './root'
|
|
35
37
|
|
|
36
38
|
export interface FileRoutesByPath {
|
|
37
39
|
// '/': {
|
package/src/index.tsx
CHANGED
|
@@ -74,12 +74,9 @@ export {
|
|
|
74
74
|
Outlet,
|
|
75
75
|
useMatchRoute,
|
|
76
76
|
MatchRoute,
|
|
77
|
-
useMatch,
|
|
78
77
|
useMatches,
|
|
79
78
|
useParentMatches,
|
|
80
79
|
useChildMatches,
|
|
81
|
-
useLoaderDeps,
|
|
82
|
-
useLoaderData,
|
|
83
80
|
isServerSideError,
|
|
84
81
|
defaultDeserializeError,
|
|
85
82
|
type RouteMatch,
|
|
@@ -88,6 +85,9 @@ export {
|
|
|
88
85
|
type UseMatchRouteOptions,
|
|
89
86
|
type MakeMatchRouteOptions,
|
|
90
87
|
} from './Matches'
|
|
88
|
+
export { useMatch } from './useMatch'
|
|
89
|
+
export { useLoaderDeps } from './useLoaderDeps'
|
|
90
|
+
export { useLoaderData } from './useLoaderData'
|
|
91
91
|
export {
|
|
92
92
|
joinPaths,
|
|
93
93
|
cleanPath,
|
|
@@ -110,8 +110,8 @@ export {
|
|
|
110
110
|
type Redirect,
|
|
111
111
|
type ResolvedRedirect,
|
|
112
112
|
} from './redirects'
|
|
113
|
+
export { rootRouteId, type RootRouteId } from './root'
|
|
113
114
|
export {
|
|
114
|
-
rootRouteId,
|
|
115
115
|
RouteApi,
|
|
116
116
|
getRouteApi,
|
|
117
117
|
Route,
|
|
@@ -122,7 +122,6 @@ export {
|
|
|
122
122
|
createRootRouteWithContext,
|
|
123
123
|
createRouteMask,
|
|
124
124
|
NotFoundRoute,
|
|
125
|
-
type RootRouteId,
|
|
126
125
|
type AnyPathParams,
|
|
127
126
|
type SearchSchemaInput,
|
|
128
127
|
type AnySearchSchema,
|
package/src/link.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import { flushSync } from 'react-dom'
|
|
3
|
-
import { useMatch } from './
|
|
3
|
+
import { useMatch } from './useMatch'
|
|
4
4
|
import { useRouterState } from './useRouterState'
|
|
5
5
|
import { useRouter } from './useRouter'
|
|
6
6
|
import { deepEqual, exactPathTest, functionalUpdate } from './utils'
|
package/src/not-found.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { CatchBoundary } from './CatchBoundary'
|
|
|
4
4
|
import { useRouterState } from './useRouterState'
|
|
5
5
|
import type { RegisteredRouter } from './router'
|
|
6
6
|
import type { RouteIds } from './routeInfo'
|
|
7
|
+
import type { ErrorInfo } from 'react'
|
|
7
8
|
|
|
8
9
|
export type NotFoundError = {
|
|
9
10
|
/**
|
|
@@ -34,7 +35,7 @@ export function isNotFound(obj: any): obj is NotFoundError {
|
|
|
34
35
|
|
|
35
36
|
export function CatchNotFound(props: {
|
|
36
37
|
fallback?: (error: NotFoundError) => React.ReactElement
|
|
37
|
-
onCatch?: (error:
|
|
38
|
+
onCatch?: (error: Error, errorInfo: ErrorInfo) => void
|
|
38
39
|
children: React.ReactNode
|
|
39
40
|
}) {
|
|
40
41
|
// TODO: Some way for the user to programmatically reset the not-found boundary?
|
|
@@ -45,9 +46,9 @@ export function CatchNotFound(props: {
|
|
|
45
46
|
return (
|
|
46
47
|
<CatchBoundary
|
|
47
48
|
getResetKey={() => resetKey}
|
|
48
|
-
onCatch={(error) => {
|
|
49
|
+
onCatch={(error, errorInfo) => {
|
|
49
50
|
if (isNotFound(error)) {
|
|
50
|
-
props.onCatch?.(error)
|
|
51
|
+
props.onCatch?.(error, errorInfo)
|
|
51
52
|
} else {
|
|
52
53
|
throw error
|
|
53
54
|
}
|
package/src/root.ts
ADDED
package/src/route.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import invariant from 'tiny-invariant'
|
|
2
|
-
import {
|
|
2
|
+
import { useMatch } from './useMatch'
|
|
3
|
+
import { useLoaderDeps } from './useLoaderDeps'
|
|
4
|
+
import { useLoaderData } from './useLoaderData'
|
|
3
5
|
import { joinPaths, trimPathLeft } from './path'
|
|
4
6
|
import { useParams } from './useParams'
|
|
5
7
|
import { useSearch } from './useSearch'
|
|
6
8
|
import { notFound } from './not-found'
|
|
7
9
|
import { useNavigate } from './useNavigate'
|
|
10
|
+
import { rootRouteId } from './root'
|
|
11
|
+
import type { RootRouteId } from './root'
|
|
8
12
|
import type { UseNavigateResult } from './useNavigate'
|
|
9
13
|
import type * as React from 'react'
|
|
10
14
|
import type { MakeRouteMatch, RouteMatch } from './Matches'
|
|
@@ -24,8 +28,6 @@ import type { BuildLocationFn, NavigateFn } from './RouterProvider'
|
|
|
24
28
|
import type { NotFoundError } from './not-found'
|
|
25
29
|
import type { LazyRoute } from './fileRoute'
|
|
26
30
|
|
|
27
|
-
export const rootRouteId = '__root__' as const
|
|
28
|
-
export type RootRouteId = typeof rootRouteId
|
|
29
31
|
export type AnyPathParams = {}
|
|
30
32
|
|
|
31
33
|
export type SearchSchemaInput = {
|
|
@@ -264,6 +266,7 @@ export type UpdatableRouteOptions<
|
|
|
264
266
|
// Filter functions that can manipulate search params *after* they are passed to links and navigate
|
|
265
267
|
// calls that match this route.
|
|
266
268
|
postSearchFilters?: Array<SearchFilter<TFullSearchSchema>>
|
|
269
|
+
onCatch?: (error: Error, errorInfo: React.ErrorInfo) => void
|
|
267
270
|
onError?: (err: any) => void
|
|
268
271
|
// These functions are called as route matches are loaded, stick around and leave the active
|
|
269
272
|
// matches
|
|
@@ -1279,7 +1282,7 @@ export type ErrorRouteProps = {
|
|
|
1279
1282
|
}
|
|
1280
1283
|
|
|
1281
1284
|
export type ErrorComponentProps = {
|
|
1282
|
-
error:
|
|
1285
|
+
error: Error
|
|
1283
1286
|
info?: { componentStack: string }
|
|
1284
1287
|
reset: () => void
|
|
1285
1288
|
}
|
package/src/router.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { createBrowserHistory, createMemoryHistory } from '@tanstack/history'
|
|
|
2
2
|
import { Store } from '@tanstack/react-store'
|
|
3
3
|
import invariant from 'tiny-invariant'
|
|
4
4
|
import warning from 'tiny-warning'
|
|
5
|
-
import { rootRouteId } from './
|
|
5
|
+
import { rootRouteId } from './root'
|
|
6
6
|
import { defaultParseSearch, defaultStringifySearch } from './searchParams'
|
|
7
7
|
import {
|
|
8
8
|
createControlledPromise,
|
|
@@ -81,6 +81,7 @@ import type { NotFoundError } from './not-found'
|
|
|
81
81
|
import type { NavigateOptions, ResolveRelativePath, ToOptions } from './link'
|
|
82
82
|
import type { NoInfer } from '@tanstack/react-store'
|
|
83
83
|
import type { DeferredPromiseState } from './defer'
|
|
84
|
+
import type { ErrorInfo } from 'react'
|
|
84
85
|
|
|
85
86
|
//
|
|
86
87
|
|
|
@@ -138,6 +139,7 @@ export interface RouterOptions<
|
|
|
138
139
|
defaultStaleTime?: number
|
|
139
140
|
defaultPreloadStaleTime?: number
|
|
140
141
|
defaultPreloadGcTime?: number
|
|
142
|
+
defaultOnCatch?: (error: Error, errorInfo: ErrorInfo) => void
|
|
141
143
|
defaultViewTransition?: boolean
|
|
142
144
|
notFoundMode?: 'root' | 'fuzzy'
|
|
143
145
|
defaultGcTime?: number
|
|
@@ -1453,13 +1455,12 @@ export class Router<
|
|
|
1453
1455
|
}))
|
|
1454
1456
|
// }
|
|
1455
1457
|
|
|
1456
|
-
rendered = true
|
|
1457
|
-
|
|
1458
1458
|
if (!(err as any).routeId) {
|
|
1459
1459
|
;(err as any).routeId = match.routeId
|
|
1460
1460
|
}
|
|
1461
1461
|
|
|
1462
1462
|
if (isRedirect(err)) {
|
|
1463
|
+
rendered = true
|
|
1463
1464
|
err = this.resolveRedirect(err)
|
|
1464
1465
|
throw err
|
|
1465
1466
|
} else if (isNotFound(err)) {
|
|
@@ -1862,6 +1863,10 @@ export class Router<
|
|
|
1862
1863
|
await triggerOnReady()
|
|
1863
1864
|
} catch (err) {
|
|
1864
1865
|
if (isRedirect(err) || isNotFound(err)) {
|
|
1866
|
+
if (isNotFound(err) && !preload) {
|
|
1867
|
+
console.log('trigger')
|
|
1868
|
+
await triggerOnReady()
|
|
1869
|
+
}
|
|
1865
1870
|
throw err
|
|
1866
1871
|
}
|
|
1867
1872
|
}
|
|
@@ -2151,7 +2156,7 @@ export class Router<
|
|
|
2151
2156
|
|
|
2152
2157
|
invariant(
|
|
2153
2158
|
_ctx,
|
|
2154
|
-
'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not.
|
|
2159
|
+
'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Please file an issue!',
|
|
2155
2160
|
)
|
|
2156
2161
|
|
|
2157
2162
|
const ctx = this.options.transformer.parse(_ctx) as HydrationCtx
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type RegisteredRouter } from './router'
|
|
2
|
+
import { type AnyRoute } from './route'
|
|
3
|
+
import { useMatch } from './useMatch'
|
|
4
|
+
import type { MakeRouteMatch } from './Matches'
|
|
5
|
+
import type { RouteIds } from './routeInfo'
|
|
6
|
+
import type { StrictOrFrom } from './utils'
|
|
7
|
+
|
|
8
|
+
export function useLoaderData<
|
|
9
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
10
|
+
TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
|
|
11
|
+
TRouteMatch extends MakeRouteMatch<TRouteTree, TFrom> = MakeRouteMatch<
|
|
12
|
+
TRouteTree,
|
|
13
|
+
TFrom
|
|
14
|
+
>,
|
|
15
|
+
TSelected = Required<TRouteMatch>['loaderData'],
|
|
16
|
+
>(
|
|
17
|
+
opts: StrictOrFrom<TFrom> & {
|
|
18
|
+
select?: (match: TRouteMatch) => TSelected
|
|
19
|
+
},
|
|
20
|
+
): TSelected {
|
|
21
|
+
return useMatch({
|
|
22
|
+
...opts,
|
|
23
|
+
select: (s) => {
|
|
24
|
+
return typeof opts.select === 'function'
|
|
25
|
+
? opts.select(s.loaderData as TRouteMatch)
|
|
26
|
+
: s.loaderData
|
|
27
|
+
},
|
|
28
|
+
}) as TSelected
|
|
29
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type RegisteredRouter } from './router'
|
|
2
|
+
import { type AnyRoute } from './route'
|
|
3
|
+
import { useMatch } from './useMatch'
|
|
4
|
+
import type { MakeRouteMatch } from './Matches'
|
|
5
|
+
import type { RouteIds } from './routeInfo'
|
|
6
|
+
import type { StrictOrFrom } from './utils'
|
|
7
|
+
|
|
8
|
+
export function useLoaderDeps<
|
|
9
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
10
|
+
TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
|
|
11
|
+
TRouteMatch extends MakeRouteMatch<TRouteTree, TFrom> = MakeRouteMatch<
|
|
12
|
+
TRouteTree,
|
|
13
|
+
TFrom
|
|
14
|
+
>,
|
|
15
|
+
TSelected = Required<TRouteMatch>['loaderDeps'],
|
|
16
|
+
>(
|
|
17
|
+
opts: StrictOrFrom<TFrom> & {
|
|
18
|
+
select?: (match: TRouteMatch) => TSelected
|
|
19
|
+
},
|
|
20
|
+
): TSelected {
|
|
21
|
+
return useMatch({
|
|
22
|
+
...opts,
|
|
23
|
+
select: (s) => {
|
|
24
|
+
return typeof opts.select === 'function'
|
|
25
|
+
? opts.select(s.loaderDeps)
|
|
26
|
+
: s.loaderDeps
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
}
|
package/src/useMatch.tsx
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import invariant from 'tiny-invariant'
|
|
3
|
+
import { useRouterState } from './useRouterState'
|
|
4
|
+
import { type RegisteredRouter } from './router'
|
|
5
|
+
import { type AnyRoute } from './route'
|
|
6
|
+
import { matchContext } from './Matches'
|
|
7
|
+
import type { MakeRouteMatch } from './Matches'
|
|
8
|
+
import type { RouteIds } from './routeInfo'
|
|
9
|
+
import type { StrictOrFrom } from './utils'
|
|
10
|
+
|
|
11
|
+
export function useMatch<
|
|
12
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
13
|
+
TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
|
|
14
|
+
TReturnIntersection extends boolean = false,
|
|
15
|
+
TRouteMatch = MakeRouteMatch<TRouteTree, TFrom, TReturnIntersection>,
|
|
16
|
+
TSelected = TRouteMatch,
|
|
17
|
+
>(
|
|
18
|
+
opts: StrictOrFrom<TFrom, TReturnIntersection> & {
|
|
19
|
+
select?: (match: TRouteMatch) => TSelected
|
|
20
|
+
},
|
|
21
|
+
): TSelected {
|
|
22
|
+
const nearestMatchId = React.useContext(matchContext)
|
|
23
|
+
|
|
24
|
+
const matchSelection = useRouterState({
|
|
25
|
+
select: (state) => {
|
|
26
|
+
const match = state.matches.find((d) =>
|
|
27
|
+
opts.from ? opts.from === d.routeId : d.id === nearestMatchId,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
invariant(
|
|
31
|
+
match,
|
|
32
|
+
`Could not find ${opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
return opts.select ? opts.select(match as any) : match
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
return matchSelection as TSelected
|
|
40
|
+
}
|
package/src/useNavigate.tsx
CHANGED
package/src/useParams.tsx
CHANGED
package/src/useRouteContext.ts
CHANGED
package/src/useSearch.tsx
CHANGED