@tanstack/react-router 0.0.1-beta.213 → 0.0.1-beta.215
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/build/cjs/CatchBoundary.js +1 -0
- package/build/cjs/CatchBoundary.js.map +1 -1
- package/build/cjs/Matches.js +14 -4
- package/build/cjs/Matches.js.map +1 -1
- package/build/cjs/RouterProvider.js +80 -69
- package/build/cjs/RouterProvider.js.map +1 -1
- package/build/cjs/fileRoute.js.map +1 -1
- package/build/cjs/index.js +1 -0
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/route.js +6 -0
- package/build/cjs/route.js.map +1 -1
- package/build/cjs/router.js.map +1 -1
- package/build/esm/index.js +101 -74
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +267 -267
- package/build/types/Matches.d.ts +4 -1
- package/build/types/RouterProvider.d.ts +2 -1
- package/build/types/fileRoute.d.ts +3 -3
- package/build/types/route.d.ts +28 -20
- package/build/types/router.d.ts +1 -1
- package/build/umd/index.development.js +101 -73
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +1 -1
- package/build/umd/index.production.js.map +1 -1
- package/package.json +2 -2
- package/src/CatchBoundary.tsx +1 -0
- package/src/Matches.tsx +31 -9
- package/src/RouterProvider.tsx +97 -89
- package/src/fileRoute.ts +10 -2
- package/src/route.ts +58 -17
- package/src/router.ts +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-router",
|
|
3
3
|
"author": "Tanner Linsley",
|
|
4
|
-
"version": "0.0.1-beta.
|
|
4
|
+
"version": "0.0.1-beta.215",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "tanstack/router",
|
|
7
7
|
"homepage": "https://tanstack.com/router",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@babel/runtime": "^7.16.7",
|
|
43
43
|
"tiny-invariant": "^1.3.1",
|
|
44
44
|
"tiny-warning": "^1.0.3",
|
|
45
|
-
"@tanstack/history": "0.0.1-beta.
|
|
45
|
+
"@tanstack/history": "0.0.1-beta.215"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "rollup --config rollup.config.js"
|
package/src/CatchBoundary.tsx
CHANGED
package/src/Matches.tsx
CHANGED
|
@@ -8,7 +8,7 @@ import { ResolveRelativePath, ToOptions } from './link'
|
|
|
8
8
|
import { AnyRoute, ReactNode, rootRouteId } from './route'
|
|
9
9
|
import { RouteById, RouteByPath, RouteIds, RoutePaths } from './routeInfo'
|
|
10
10
|
import { RegisteredRouter } from './router'
|
|
11
|
-
import { NoInfer, StrictOrFrom } from './utils'
|
|
11
|
+
import { NoInfer, StrictOrFrom, functionalUpdate } from './utils'
|
|
12
12
|
|
|
13
13
|
export function Matches() {
|
|
14
14
|
const { routesById, state } = useRouter()
|
|
@@ -16,7 +16,7 @@ export function Matches() {
|
|
|
16
16
|
|
|
17
17
|
const locationKey = useRouterState().location.state.key
|
|
18
18
|
|
|
19
|
-
const route = routesById[rootRouteId]
|
|
19
|
+
const route = routesById[rootRouteId]!
|
|
20
20
|
|
|
21
21
|
const errorComponent = React.useCallback(
|
|
22
22
|
(props: any) => {
|
|
@@ -58,7 +58,7 @@ export function Match({ matches }: { matches: RouteMatch[] }) {
|
|
|
58
58
|
const { options, routesById } = useRouter()
|
|
59
59
|
const match = matches[0]!
|
|
60
60
|
const routeId = match?.routeId
|
|
61
|
-
const route = routesById[routeId]
|
|
61
|
+
const route = routesById[routeId]!
|
|
62
62
|
const locationKey = useRouterState().location.state?.key
|
|
63
63
|
|
|
64
64
|
const PendingComponent = (route.options.pendingComponent ??
|
|
@@ -70,9 +70,9 @@ export function Match({ matches }: { matches: RouteMatch[] }) {
|
|
|
70
70
|
options.defaultErrorComponent ??
|
|
71
71
|
ErrorComponent
|
|
72
72
|
|
|
73
|
-
const ResolvedSuspenseBoundary =
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
const ResolvedSuspenseBoundary = route.options.wrapInSuspense
|
|
74
|
+
? React.Suspense
|
|
75
|
+
: SafeFragment
|
|
76
76
|
|
|
77
77
|
const errorComponent = React.useCallback(
|
|
78
78
|
(props: any) => {
|
|
@@ -112,7 +112,7 @@ export function Match({ matches }: { matches: RouteMatch[] }) {
|
|
|
112
112
|
}
|
|
113
113
|
function MatchInner({ match }: { match: RouteMatch }): any {
|
|
114
114
|
const { options, routesById } = useRouter()
|
|
115
|
-
const route = routesById[match.routeId]
|
|
115
|
+
const route = routesById[match.routeId]!
|
|
116
116
|
|
|
117
117
|
if (match.status === 'error') {
|
|
118
118
|
throw match.error
|
|
@@ -131,7 +131,8 @@ function MatchInner({ match }: { match: RouteMatch }): any {
|
|
|
131
131
|
useRouteContext: route.useRouteContext as any,
|
|
132
132
|
useSearch: route.useSearch,
|
|
133
133
|
useParams: route.useParams as any,
|
|
134
|
-
|
|
134
|
+
useLoaderData: route.useLoaderData,
|
|
135
|
+
})
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
return <Outlet />
|
|
@@ -248,7 +249,7 @@ export function useMatch<
|
|
|
248
249
|
opts: StrictOrFrom<TFrom> & {
|
|
249
250
|
select?: (match: TRouteMatchState) => TSelected
|
|
250
251
|
},
|
|
251
|
-
): TStrict extends true ?
|
|
252
|
+
): TStrict extends true ? TSelected : TSelected | undefined {
|
|
252
253
|
const nearestMatch = React.useContext(matchesContext)[0]!
|
|
253
254
|
const nearestMatchRouteId = nearestMatch?.routeId
|
|
254
255
|
|
|
@@ -313,3 +314,24 @@ export function useMatches<T = RouteMatch[]>(opts?: {
|
|
|
313
314
|
},
|
|
314
315
|
})
|
|
315
316
|
}
|
|
317
|
+
|
|
318
|
+
export function useLoaderData<
|
|
319
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
320
|
+
TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
|
|
321
|
+
TStrict extends boolean = true,
|
|
322
|
+
TRouteMatch extends RouteMatch<TRouteTree, TFrom> = RouteMatch<
|
|
323
|
+
TRouteTree,
|
|
324
|
+
TFrom
|
|
325
|
+
>,
|
|
326
|
+
TSelected = TRouteMatch['loaderData'],
|
|
327
|
+
>(
|
|
328
|
+
opts: StrictOrFrom<TFrom> & {
|
|
329
|
+
select?: (match: TRouteMatch) => TSelected
|
|
330
|
+
},
|
|
331
|
+
): TStrict extends true ? TSelected : TSelected | undefined {
|
|
332
|
+
const match = useMatch({ ...opts, select: undefined })!
|
|
333
|
+
|
|
334
|
+
return typeof opts.select === 'function'
|
|
335
|
+
? opts.select(match?.loaderData)
|
|
336
|
+
: match?.loaderData
|
|
337
|
+
}
|
package/src/RouterProvider.tsx
CHANGED
|
@@ -83,16 +83,13 @@ export type BuildLinkFn<TRouteTree extends AnyRoute> = <
|
|
|
83
83
|
) => LinkInfo
|
|
84
84
|
|
|
85
85
|
export type NavigateFn<TRouteTree extends AnyRoute> = <
|
|
86
|
-
TRouteTree extends AnyRoute,
|
|
87
86
|
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
88
87
|
TTo extends string = '',
|
|
89
88
|
TMaskFrom extends RoutePaths<TRouteTree> = TFrom,
|
|
90
89
|
TMaskTo extends string = '',
|
|
91
|
-
>(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
...rest
|
|
95
|
-
}: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>) => Promise<void>
|
|
90
|
+
>(
|
|
91
|
+
opts: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
|
|
92
|
+
) => Promise<void>
|
|
96
93
|
|
|
97
94
|
export type MatchRouteFn<TRouteTree extends AnyRoute> = <
|
|
98
95
|
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
@@ -250,12 +247,14 @@ export function RouterProvider<
|
|
|
250
247
|
getInitialRouterState(latestLocationRef.current),
|
|
251
248
|
)
|
|
252
249
|
const [isTransitioning, startReactTransition] = React.useTransition()
|
|
250
|
+
const pendingMatchesRef = React.useRef<AnyRouteMatch[]>([])
|
|
253
251
|
|
|
254
252
|
const state = React.useMemo<RouterState<TRouteTree>>(
|
|
255
253
|
() => ({
|
|
256
254
|
...preState,
|
|
257
255
|
status: isTransitioning ? 'pending' : 'idle',
|
|
258
256
|
location: isTransitioning ? latestLocationRef.current : preState.location,
|
|
257
|
+
pendingMatches: pendingMatchesRef.current,
|
|
259
258
|
}),
|
|
260
259
|
[preState, isTransitioning],
|
|
261
260
|
)
|
|
@@ -268,6 +267,8 @@ export function RouterProvider<
|
|
|
268
267
|
toLocation: state.location,
|
|
269
268
|
pathChanged: state.location!.href !== state.resolvedLocation?.href,
|
|
270
269
|
})
|
|
270
|
+
pendingMatchesRef.current = []
|
|
271
|
+
|
|
271
272
|
setState((s) => ({
|
|
272
273
|
...s,
|
|
273
274
|
resolvedLocation: s.location,
|
|
@@ -464,7 +465,7 @@ export function RouterProvider<
|
|
|
464
465
|
|
|
465
466
|
// Create a fresh route match
|
|
466
467
|
const hasLoaders = !!(
|
|
467
|
-
route.options.
|
|
468
|
+
route.options.loader ||
|
|
468
469
|
componentTypes.some((d) => (route.options[d] as any)?.preload)
|
|
469
470
|
)
|
|
470
471
|
|
|
@@ -938,10 +939,6 @@ export function RouterProvider<
|
|
|
938
939
|
const parentMatchPromise = matchPromises[index - 1]
|
|
939
940
|
const route = looseRoutesById[match.routeId]!
|
|
940
941
|
|
|
941
|
-
if (match.isFetching) {
|
|
942
|
-
return getRouteMatch(state, match.id)?.loadPromise
|
|
943
|
-
}
|
|
944
|
-
|
|
945
942
|
const handleIfRedirect = (err: any) => {
|
|
946
943
|
if (isRedirect(err)) {
|
|
947
944
|
if (!preload) {
|
|
@@ -952,90 +949,101 @@ export function RouterProvider<
|
|
|
952
949
|
return false
|
|
953
950
|
}
|
|
954
951
|
|
|
955
|
-
const load = async () => {
|
|
956
|
-
try {
|
|
957
|
-
const componentsPromise = Promise.all(
|
|
958
|
-
componentTypes.map(async (type) => {
|
|
959
|
-
const component = route.options[type]
|
|
960
|
-
|
|
961
|
-
if ((component as any)?.preload) {
|
|
962
|
-
await (component as any).preload()
|
|
963
|
-
}
|
|
964
|
-
}),
|
|
965
|
-
)
|
|
966
|
-
|
|
967
|
-
const loaderPromise = route.options.load?.({
|
|
968
|
-
params: match.params,
|
|
969
|
-
search: match.search,
|
|
970
|
-
preload: !!preload,
|
|
971
|
-
parentMatchPromise,
|
|
972
|
-
abortController: match.abortController,
|
|
973
|
-
context: match.context,
|
|
974
|
-
location: state.location,
|
|
975
|
-
navigate: (opts) =>
|
|
976
|
-
navigate({ ...opts, from: match.pathname }),
|
|
977
|
-
})
|
|
978
|
-
|
|
979
|
-
const [_, loaderContext] = await Promise.all([
|
|
980
|
-
componentsPromise,
|
|
981
|
-
loaderPromise,
|
|
982
|
-
])
|
|
983
|
-
if ((latestPromise = checkLatest())) return await latestPromise
|
|
984
|
-
|
|
985
|
-
matches[index] = match = {
|
|
986
|
-
...match,
|
|
987
|
-
error: undefined,
|
|
988
|
-
status: 'success',
|
|
989
|
-
isFetching: false,
|
|
990
|
-
updatedAt: Date.now(),
|
|
991
|
-
}
|
|
992
|
-
} catch (error) {
|
|
993
|
-
if ((latestPromise = checkLatest())) return await latestPromise
|
|
994
|
-
if (handleIfRedirect(error)) return
|
|
995
|
-
|
|
996
|
-
try {
|
|
997
|
-
route.options.onError?.(error)
|
|
998
|
-
} catch (onErrorError) {
|
|
999
|
-
error = onErrorError
|
|
1000
|
-
if (handleIfRedirect(onErrorError)) return
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
matches[index] = match = {
|
|
1004
|
-
...match,
|
|
1005
|
-
error,
|
|
1006
|
-
status: 'error',
|
|
1007
|
-
isFetching: false,
|
|
1008
|
-
updatedAt: Date.now(),
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
if (!preload) {
|
|
1013
|
-
setState((s) => ({
|
|
1014
|
-
...s,
|
|
1015
|
-
matches: s.matches.map((d) =>
|
|
1016
|
-
d.id === match.id ? match : d,
|
|
1017
|
-
),
|
|
1018
|
-
}))
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
952
|
let loadPromise: Promise<void> | undefined
|
|
1023
953
|
|
|
1024
954
|
matches[index] = match = {
|
|
1025
955
|
...match,
|
|
1026
|
-
isFetching: true,
|
|
1027
956
|
fetchedAt: Date.now(),
|
|
1028
957
|
invalid: false,
|
|
1029
958
|
}
|
|
1030
959
|
|
|
1031
|
-
|
|
960
|
+
if (match.isFetching) {
|
|
961
|
+
loadPromise = getRouteMatch(state, match.id)?.loadPromise
|
|
962
|
+
} else {
|
|
963
|
+
matches[index] = match = {
|
|
964
|
+
...match,
|
|
965
|
+
isFetching: true,
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
const componentsPromise = Promise.all(
|
|
969
|
+
componentTypes.map(async (type) => {
|
|
970
|
+
const component = route.options[type]
|
|
971
|
+
|
|
972
|
+
if ((component as any)?.preload) {
|
|
973
|
+
await (component as any).preload()
|
|
974
|
+
}
|
|
975
|
+
}),
|
|
976
|
+
)
|
|
977
|
+
|
|
978
|
+
const loaderPromise = route.options.loader?.({
|
|
979
|
+
params: match.params,
|
|
980
|
+
search: match.search,
|
|
981
|
+
preload: !!preload,
|
|
982
|
+
parentMatchPromise,
|
|
983
|
+
abortController: match.abortController,
|
|
984
|
+
context: match.context,
|
|
985
|
+
location: state.location,
|
|
986
|
+
navigate: (opts) =>
|
|
987
|
+
navigate({ ...opts, from: match.pathname } as any),
|
|
988
|
+
})
|
|
989
|
+
|
|
990
|
+
loadPromise = Promise.all([
|
|
991
|
+
componentsPromise,
|
|
992
|
+
loaderPromise,
|
|
993
|
+
]).then((d) => d[1])
|
|
994
|
+
}
|
|
1032
995
|
|
|
1033
996
|
matches[index] = match = {
|
|
1034
997
|
...match,
|
|
1035
998
|
loadPromise,
|
|
1036
999
|
}
|
|
1037
1000
|
|
|
1038
|
-
|
|
1001
|
+
if (!preload) {
|
|
1002
|
+
setState((s) => ({
|
|
1003
|
+
...s,
|
|
1004
|
+
matches: s.matches.map((d) => (d.id === match.id ? match : d)),
|
|
1005
|
+
}))
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
try {
|
|
1009
|
+
const loaderData = await loadPromise
|
|
1010
|
+
if ((latestPromise = checkLatest())) return await latestPromise
|
|
1011
|
+
|
|
1012
|
+
matches[index] = match = {
|
|
1013
|
+
...match,
|
|
1014
|
+
error: undefined,
|
|
1015
|
+
status: 'success',
|
|
1016
|
+
isFetching: false,
|
|
1017
|
+
updatedAt: Date.now(),
|
|
1018
|
+
loaderData,
|
|
1019
|
+
loadPromise: undefined,
|
|
1020
|
+
}
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
if ((latestPromise = checkLatest())) return await latestPromise
|
|
1023
|
+
if (handleIfRedirect(error)) return
|
|
1024
|
+
|
|
1025
|
+
try {
|
|
1026
|
+
route.options.onError?.(error)
|
|
1027
|
+
} catch (onErrorError) {
|
|
1028
|
+
error = onErrorError
|
|
1029
|
+
if (handleIfRedirect(onErrorError)) return
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
matches[index] = match = {
|
|
1033
|
+
...match,
|
|
1034
|
+
error,
|
|
1035
|
+
status: 'error',
|
|
1036
|
+
isFetching: false,
|
|
1037
|
+
updatedAt: Date.now(),
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
if (!preload) {
|
|
1042
|
+
setState((s) => ({
|
|
1043
|
+
...s,
|
|
1044
|
+
matches: s.matches.map((d) => (d.id === match.id ? match : d)),
|
|
1045
|
+
}))
|
|
1046
|
+
}
|
|
1039
1047
|
})(),
|
|
1040
1048
|
)
|
|
1041
1049
|
})
|
|
@@ -1071,6 +1079,8 @@ export function RouterProvider<
|
|
|
1071
1079
|
},
|
|
1072
1080
|
)
|
|
1073
1081
|
|
|
1082
|
+
pendingMatchesRef.current = matches
|
|
1083
|
+
|
|
1074
1084
|
const previousMatches = state.matches
|
|
1075
1085
|
|
|
1076
1086
|
// Ingest the new matches
|
|
@@ -1099,13 +1109,13 @@ export function RouterProvider<
|
|
|
1099
1109
|
}
|
|
1100
1110
|
|
|
1101
1111
|
const exitingMatchIds = previousMatches.filter(
|
|
1102
|
-
(id) => !
|
|
1112
|
+
(id) => !pendingMatchesRef.current.includes(id),
|
|
1103
1113
|
)
|
|
1104
|
-
const enteringMatchIds =
|
|
1114
|
+
const enteringMatchIds = pendingMatchesRef.current.filter(
|
|
1105
1115
|
(id) => !previousMatches.includes(id),
|
|
1106
1116
|
)
|
|
1107
1117
|
const stayingMatchIds = previousMatches.filter((id) =>
|
|
1108
|
-
|
|
1118
|
+
pendingMatchesRef.current.includes(id),
|
|
1109
1119
|
)
|
|
1110
1120
|
|
|
1111
1121
|
// setState((s) => ({
|
|
@@ -1326,10 +1336,7 @@ export function RouterProvider<
|
|
|
1326
1336
|
}
|
|
1327
1337
|
}, [history])
|
|
1328
1338
|
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
if (initialLoad.current) {
|
|
1332
|
-
initialLoad.current = false
|
|
1339
|
+
React.useLayoutEffect(() => {
|
|
1333
1340
|
startReactTransition(() => {
|
|
1334
1341
|
try {
|
|
1335
1342
|
load()
|
|
@@ -1337,7 +1344,7 @@ export function RouterProvider<
|
|
|
1337
1344
|
console.error(err)
|
|
1338
1345
|
}
|
|
1339
1346
|
})
|
|
1340
|
-
}
|
|
1347
|
+
}, [])
|
|
1341
1348
|
|
|
1342
1349
|
const matchRoute = useStableCallback<MatchRouteFn<TRouteTree>>(
|
|
1343
1350
|
(location, opts) => {
|
|
@@ -1454,6 +1461,7 @@ export interface RouteMatch<
|
|
|
1454
1461
|
searchError: unknown
|
|
1455
1462
|
updatedAt: number
|
|
1456
1463
|
loadPromise?: Promise<void>
|
|
1464
|
+
loaderData?: RouteById<TRouteTree, TRouteId>['types']['loaderData']
|
|
1457
1465
|
__resolveLoadPromise?: () => void
|
|
1458
1466
|
context: RouteById<TRouteTree, TRouteId>['types']['allContext']
|
|
1459
1467
|
routeSearch: RouteById<TRouteTree, TRouteId>['types']['searchSchema']
|
package/src/fileRoute.ts
CHANGED
|
@@ -104,6 +104,7 @@ export class FileRoute<
|
|
|
104
104
|
Assign<IsAny<TParentRoute['types']['allContext'], {}>, TRouteContext>
|
|
105
105
|
>,
|
|
106
106
|
TRouterContext extends RouteConstraints['TRouterContext'] = AnyContext,
|
|
107
|
+
TLoaderData extends any = unknown,
|
|
107
108
|
TChildren extends RouteConstraints['TChildren'] = unknown,
|
|
108
109
|
TRouteTree extends RouteConstraints['TRouteTree'] = AnyRoute,
|
|
109
110
|
>(
|
|
@@ -117,11 +118,17 @@ export class FileRoute<
|
|
|
117
118
|
TParams,
|
|
118
119
|
TAllParams,
|
|
119
120
|
TRouteContext,
|
|
120
|
-
TContext
|
|
121
|
+
TContext,
|
|
122
|
+
TLoaderData
|
|
121
123
|
>,
|
|
122
124
|
'getParentRoute' | 'path' | 'id'
|
|
123
125
|
> &
|
|
124
|
-
UpdatableRouteOptions<
|
|
126
|
+
UpdatableRouteOptions<
|
|
127
|
+
TFullSearchSchema,
|
|
128
|
+
TAllParams,
|
|
129
|
+
TContext,
|
|
130
|
+
TLoaderData
|
|
131
|
+
>,
|
|
125
132
|
): Route<
|
|
126
133
|
TParentRoute,
|
|
127
134
|
TPath,
|
|
@@ -135,6 +142,7 @@ export class FileRoute<
|
|
|
135
142
|
TRouteContext,
|
|
136
143
|
TContext,
|
|
137
144
|
TRouterContext,
|
|
145
|
+
TLoaderData,
|
|
138
146
|
TChildren,
|
|
139
147
|
TRouteTree
|
|
140
148
|
> => {
|
package/src/route.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { HistoryLocation } from '@tanstack/history'
|
|
2
2
|
import * as React from 'react'
|
|
3
3
|
import invariant from 'tiny-invariant'
|
|
4
|
-
import { useMatch } from './Matches'
|
|
4
|
+
import { useLoaderData, useMatch } from './Matches'
|
|
5
5
|
import { AnyRouteMatch } from './RouterProvider'
|
|
6
6
|
import { NavigateOptions, ParsePathParams, ToSubOptions } from './link'
|
|
7
7
|
import { ParsedLocation } from './location'
|
|
@@ -63,6 +63,7 @@ export type RouteOptions<
|
|
|
63
63
|
TAllParams extends AnyPathParams = TParams,
|
|
64
64
|
TRouteContext extends RouteContext = RouteContext,
|
|
65
65
|
TAllContext extends Record<string, any> = AnyContext,
|
|
66
|
+
TLoaderData extends any = unknown,
|
|
66
67
|
> = BaseRouteOptions<
|
|
67
68
|
TParentRoute,
|
|
68
69
|
TCustomId,
|
|
@@ -72,9 +73,17 @@ export type RouteOptions<
|
|
|
72
73
|
TParams,
|
|
73
74
|
TAllParams,
|
|
74
75
|
TRouteContext,
|
|
75
|
-
TAllContext
|
|
76
|
+
TAllContext,
|
|
77
|
+
TLoaderData
|
|
76
78
|
> &
|
|
77
|
-
NoInfer<
|
|
79
|
+
NoInfer<
|
|
80
|
+
UpdatableRouteOptions<
|
|
81
|
+
TFullSearchSchema,
|
|
82
|
+
TAllParams,
|
|
83
|
+
TAllContext,
|
|
84
|
+
TLoaderData
|
|
85
|
+
>
|
|
86
|
+
>
|
|
78
87
|
|
|
79
88
|
export type ParamsFallback<
|
|
80
89
|
TPath extends string,
|
|
@@ -95,6 +104,7 @@ export type BaseRouteOptions<
|
|
|
95
104
|
TAllParams = ParamsFallback<TPath, TParams>,
|
|
96
105
|
TRouteContext extends RouteContext = RouteContext,
|
|
97
106
|
TAllContext extends Record<string, any> = AnyContext,
|
|
107
|
+
TLoaderData extends any = unknown,
|
|
98
108
|
> = RoutePathOptions<TCustomId, TPath> & {
|
|
99
109
|
getParentRoute: () => TParentRoute
|
|
100
110
|
validateSearch?: SearchSchemaValidator<TSearchSchema>
|
|
@@ -119,11 +129,12 @@ export type BaseRouteOptions<
|
|
|
119
129
|
TRouteContext
|
|
120
130
|
>
|
|
121
131
|
}) & {
|
|
122
|
-
|
|
132
|
+
loader?: RouteLoadFn<
|
|
123
133
|
TAllParams,
|
|
124
134
|
TFullSearchSchema,
|
|
125
135
|
NoInfer<TAllContext>,
|
|
126
|
-
NoInfer<TRouteContext
|
|
136
|
+
NoInfer<TRouteContext>,
|
|
137
|
+
TLoaderData
|
|
127
138
|
>
|
|
128
139
|
} & (
|
|
129
140
|
| {
|
|
@@ -163,6 +174,7 @@ export type UpdatableRouteOptions<
|
|
|
163
174
|
TFullSearchSchema extends Record<string, any>,
|
|
164
175
|
TAllParams extends AnyPathParams,
|
|
165
176
|
TAllContext extends AnyContext,
|
|
177
|
+
TLoaderData extends any = unknown,
|
|
166
178
|
> = MetaOptions & {
|
|
167
179
|
// test?: (args: TAllContext) => void
|
|
168
180
|
// If true, this route will be matched as case-sensitive
|
|
@@ -170,7 +182,12 @@ export type UpdatableRouteOptions<
|
|
|
170
182
|
// If true, this route will be forcefully wrapped in a suspense boundary
|
|
171
183
|
wrapInSuspense?: boolean
|
|
172
184
|
// The content to be rendered when the route is matched. If no component is provided, defaults to `<Outlet />`
|
|
173
|
-
component?: RouteComponent<
|
|
185
|
+
component?: RouteComponent<
|
|
186
|
+
TFullSearchSchema,
|
|
187
|
+
TAllParams,
|
|
188
|
+
TAllContext,
|
|
189
|
+
TLoaderData
|
|
190
|
+
>
|
|
174
191
|
// The content to be rendered when the route encounters an error
|
|
175
192
|
errorComponent?: ErrorRouteComponent<
|
|
176
193
|
TFullSearchSchema,
|
|
@@ -244,8 +261,9 @@ export type RouteLoadFn<
|
|
|
244
261
|
TFullSearchSchema extends Record<string, any> = {},
|
|
245
262
|
TAllContext extends Record<string, any> = AnyContext,
|
|
246
263
|
TRouteContext extends Record<string, any> = AnyContext,
|
|
264
|
+
TLoaderData extends any = unknown,
|
|
247
265
|
> = (
|
|
248
|
-
match:
|
|
266
|
+
match: LoaderFnContext<
|
|
249
267
|
TAllParams,
|
|
250
268
|
TFullSearchSchema,
|
|
251
269
|
TAllContext,
|
|
@@ -253,9 +271,9 @@ export type RouteLoadFn<
|
|
|
253
271
|
> & {
|
|
254
272
|
parentMatchPromise?: Promise<void>
|
|
255
273
|
},
|
|
256
|
-
) =>
|
|
274
|
+
) => Promise<TLoaderData> | TLoaderData
|
|
257
275
|
|
|
258
|
-
export interface
|
|
276
|
+
export interface LoaderFnContext<
|
|
259
277
|
TAllParams = {},
|
|
260
278
|
TFullSearchSchema extends Record<string, any> = {},
|
|
261
279
|
TAllContext extends Record<string, any> = AnyContext,
|
|
@@ -378,6 +396,7 @@ export class Route<
|
|
|
378
396
|
Assign<IsAny<TParentRoute['types']['allContext'], {}>, TRouteContext>
|
|
379
397
|
>,
|
|
380
398
|
TRouterContext extends RouteConstraints['TRouterContext'] = AnyContext,
|
|
399
|
+
TLoaderData extends any = unknown,
|
|
381
400
|
TChildren extends RouteConstraints['TChildren'] = unknown,
|
|
382
401
|
TRouteTree extends RouteConstraints['TRouteTree'] = AnyRoute,
|
|
383
402
|
> {
|
|
@@ -391,7 +410,8 @@ export class Route<
|
|
|
391
410
|
TParams,
|
|
392
411
|
TAllParams,
|
|
393
412
|
TRouteContext,
|
|
394
|
-
TAllContext
|
|
413
|
+
TAllContext,
|
|
414
|
+
TLoaderData
|
|
395
415
|
>
|
|
396
416
|
|
|
397
417
|
test!: Expand<
|
|
@@ -422,7 +442,8 @@ export class Route<
|
|
|
422
442
|
TParams,
|
|
423
443
|
TAllParams,
|
|
424
444
|
TRouteContext,
|
|
425
|
-
TAllContext
|
|
445
|
+
TAllContext,
|
|
446
|
+
TLoaderData
|
|
426
447
|
>,
|
|
427
448
|
) {
|
|
428
449
|
this.options = (options as any) || {}
|
|
@@ -446,6 +467,7 @@ export class Route<
|
|
|
446
467
|
children: TChildren
|
|
447
468
|
routeTree: TRouteTree
|
|
448
469
|
routerContext: TRouterContext
|
|
470
|
+
loaderData: TLoaderData
|
|
449
471
|
}
|
|
450
472
|
|
|
451
473
|
init = (opts: { originalIndex: number }) => {
|
|
@@ -460,7 +482,8 @@ export class Route<
|
|
|
460
482
|
TParams,
|
|
461
483
|
TAllParams,
|
|
462
484
|
TRouteContext,
|
|
463
|
-
TAllContext
|
|
485
|
+
TAllContext,
|
|
486
|
+
TLoaderData
|
|
464
487
|
> &
|
|
465
488
|
RoutePathOptionsIntersection<TCustomId, TPath>
|
|
466
489
|
|
|
@@ -542,7 +565,8 @@ export class Route<
|
|
|
542
565
|
TAllParams,
|
|
543
566
|
Expand<
|
|
544
567
|
Assign<IsAny<TParentRoute['types']['allContext'], {}>, TRouteContext>
|
|
545
|
-
|
|
568
|
+
>,
|
|
569
|
+
TLoaderData
|
|
546
570
|
>,
|
|
547
571
|
) => {
|
|
548
572
|
Object.assign(this.options, options)
|
|
@@ -578,6 +602,11 @@ export class Route<
|
|
|
578
602
|
}): TSelected => {
|
|
579
603
|
return useParams({ ...opts, from: this.id } as any)
|
|
580
604
|
}
|
|
605
|
+
useLoaderData = <TSelected = TLoaderData>(opts?: {
|
|
606
|
+
select?: (search: TLoaderData) => TSelected
|
|
607
|
+
}): TSelected => {
|
|
608
|
+
return useLoaderData({ ...opts, from: this.id } as any) as any
|
|
609
|
+
}
|
|
581
610
|
}
|
|
582
611
|
|
|
583
612
|
export type AnyRootRoute = RootRoute<any, any, any>
|
|
@@ -586,6 +615,7 @@ export function rootRouteWithContext<TRouterContext extends {}>() {
|
|
|
586
615
|
return <
|
|
587
616
|
TSearchSchema extends Record<string, any> = {},
|
|
588
617
|
TRouteContext extends RouteContext = RouteContext,
|
|
618
|
+
TLoaderData extends any = unknown,
|
|
589
619
|
>(
|
|
590
620
|
options?: Omit<
|
|
591
621
|
RouteOptions<
|
|
@@ -597,7 +627,8 @@ export function rootRouteWithContext<TRouterContext extends {}>() {
|
|
|
597
627
|
{}, // TParams
|
|
598
628
|
{}, // TAllParams
|
|
599
629
|
TRouteContext, // TRouteContext
|
|
600
|
-
Assign<TRouterContext, TRouteContext
|
|
630
|
+
Assign<TRouterContext, TRouteContext>, // TAllContext
|
|
631
|
+
TLoaderData // TLoaderData
|
|
601
632
|
>,
|
|
602
633
|
| 'path'
|
|
603
634
|
| 'id'
|
|
@@ -615,6 +646,7 @@ export class RootRoute<
|
|
|
615
646
|
TSearchSchema extends Record<string, any> = {},
|
|
616
647
|
TRouteContext extends RouteContext = RouteContext,
|
|
617
648
|
TRouterContext extends {} = {},
|
|
649
|
+
TLoaderData extends any = unknown,
|
|
618
650
|
> extends Route<
|
|
619
651
|
any, // TParentRoute
|
|
620
652
|
'/', // TPath
|
|
@@ -628,6 +660,7 @@ export class RootRoute<
|
|
|
628
660
|
TRouteContext, // TRouteContext
|
|
629
661
|
Expand<Assign<TRouterContext, TRouteContext>>, // TAllContext
|
|
630
662
|
TRouterContext, // TRouterContext
|
|
663
|
+
TLoaderData,
|
|
631
664
|
any, // TChildren
|
|
632
665
|
any // TRouteTree
|
|
633
666
|
> {
|
|
@@ -642,7 +675,8 @@ export class RootRoute<
|
|
|
642
675
|
{}, // TParams
|
|
643
676
|
{}, // TAllParams
|
|
644
677
|
TRouteContext, // TRouteContext
|
|
645
|
-
Assign<TRouterContext, TRouteContext
|
|
678
|
+
Assign<TRouterContext, TRouteContext>, // TAllContext
|
|
679
|
+
TLoaderData
|
|
646
680
|
>,
|
|
647
681
|
| 'path'
|
|
648
682
|
| 'id'
|
|
@@ -720,6 +754,7 @@ export type RouteProps<
|
|
|
720
754
|
TFullSearchSchema extends Record<string, any> = AnySearchSchema,
|
|
721
755
|
TAllParams extends AnyPathParams = AnyPathParams,
|
|
722
756
|
TAllContext extends Record<string, any> = AnyContext,
|
|
757
|
+
TLoaderData extends any = unknown,
|
|
723
758
|
> = {
|
|
724
759
|
useMatch: <TSelected = TAllContext>(opts?: {
|
|
725
760
|
select?: (search: TAllContext) => TSelected
|
|
@@ -733,6 +768,9 @@ export type RouteProps<
|
|
|
733
768
|
useParams: <TSelected = TAllParams>(opts?: {
|
|
734
769
|
select?: (search: TAllParams) => TSelected
|
|
735
770
|
}) => TSelected
|
|
771
|
+
useLoaderData: <TSelected = TLoaderData>(opts?: {
|
|
772
|
+
select?: (search: TLoaderData) => TSelected
|
|
773
|
+
}) => TSelected
|
|
736
774
|
}
|
|
737
775
|
|
|
738
776
|
export type ErrorRouteProps<
|
|
@@ -765,7 +803,10 @@ export type RouteComponent<
|
|
|
765
803
|
TFullSearchSchema extends Record<string, any>,
|
|
766
804
|
TAllParams extends AnyPathParams,
|
|
767
805
|
TAllContext extends Record<string, any>,
|
|
768
|
-
|
|
806
|
+
TLoaderData extends any = unknown,
|
|
807
|
+
> = AsyncRouteComponent<
|
|
808
|
+
RouteProps<TFullSearchSchema, TAllParams, TAllContext, TLoaderData>
|
|
809
|
+
>
|
|
769
810
|
|
|
770
811
|
export type ErrorRouteComponent<
|
|
771
812
|
TFullSearchSchema extends Record<string, any>,
|
|
@@ -783,4 +824,4 @@ export type PendingRouteComponent<
|
|
|
783
824
|
PendingRouteProps<TFullSearchSchema, TAllParams, TAllContext>
|
|
784
825
|
>
|
|
785
826
|
|
|
786
|
-
export type AnyRouteComponent = RouteComponent<any, any, any>
|
|
827
|
+
export type AnyRouteComponent = RouteComponent<any, any, any, any>
|
package/src/router.ts
CHANGED