@tanstack/react-router 1.167.5 → 1.168.1
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 +118 -52
- package/dist/cjs/Match.cjs.map +1 -1
- package/dist/cjs/Matches.cjs +20 -20
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/Scripts.cjs +36 -32
- package/dist/cjs/Scripts.cjs.map +1 -1
- package/dist/cjs/Transitioner.cjs +10 -16
- package/dist/cjs/Transitioner.cjs.map +1 -1
- package/dist/cjs/headContentUtils.cjs +147 -59
- package/dist/cjs/headContentUtils.cjs.map +1 -1
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.dev.cjs +1 -1
- package/dist/cjs/link.cjs +34 -29
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/not-found.cjs +20 -2
- package/dist/cjs/not-found.cjs.map +1 -1
- package/dist/cjs/router.cjs +2 -1
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/routerStores.cjs +21 -0
- package/dist/cjs/routerStores.cjs.map +1 -0
- package/dist/cjs/routerStores.d.cts +7 -0
- package/dist/cjs/ssr/RouterClient.cjs +1 -1
- package/dist/cjs/ssr/RouterClient.cjs.map +1 -1
- package/dist/cjs/ssr/renderRouterToStream.cjs +2 -2
- package/dist/cjs/ssr/renderRouterToStream.cjs.map +1 -1
- package/dist/cjs/ssr/renderRouterToString.cjs +1 -1
- package/dist/cjs/ssr/renderRouterToString.cjs.map +1 -1
- package/dist/cjs/useCanGoBack.cjs +7 -2
- package/dist/cjs/useCanGoBack.cjs.map +1 -1
- package/dist/cjs/useLocation.cjs +21 -2
- package/dist/cjs/useLocation.cjs.map +1 -1
- package/dist/cjs/useMatch.cjs +29 -9
- package/dist/cjs/useMatch.cjs.map +1 -1
- package/dist/cjs/useRouterState.cjs +2 -2
- package/dist/cjs/useRouterState.cjs.map +1 -1
- package/dist/esm/Match.js +118 -52
- package/dist/esm/Match.js.map +1 -1
- package/dist/esm/Matches.js +21 -21
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/Scripts.js +36 -32
- package/dist/esm/Scripts.js.map +1 -1
- package/dist/esm/Transitioner.js +10 -16
- package/dist/esm/Transitioner.js.map +1 -1
- package/dist/esm/headContentUtils.js +148 -60
- package/dist/esm/headContentUtils.js.map +1 -1
- package/dist/esm/index.dev.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/link.js +34 -29
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/not-found.js +20 -2
- package/dist/esm/not-found.js.map +1 -1
- package/dist/esm/router.js +2 -1
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/routerStores.d.ts +7 -0
- package/dist/esm/routerStores.js +20 -0
- package/dist/esm/routerStores.js.map +1 -0
- package/dist/esm/ssr/RouterClient.js +1 -1
- package/dist/esm/ssr/RouterClient.js.map +1 -1
- package/dist/esm/ssr/renderRouterToStream.js +2 -2
- package/dist/esm/ssr/renderRouterToStream.js.map +1 -1
- package/dist/esm/ssr/renderRouterToString.js +1 -1
- package/dist/esm/ssr/renderRouterToString.js.map +1 -1
- package/dist/esm/useCanGoBack.js +6 -2
- package/dist/esm/useCanGoBack.js.map +1 -1
- package/dist/esm/useLocation.js +20 -2
- package/dist/esm/useLocation.js.map +1 -1
- package/dist/esm/useMatch.js +29 -9
- package/dist/esm/useMatch.js.map +1 -1
- package/dist/esm/useRouterState.js +2 -2
- package/dist/esm/useRouterState.js.map +1 -1
- package/dist/llms/rules/api.d.ts +1 -1
- package/dist/llms/rules/api.js +3 -9
- package/package.json +3 -3
- package/src/Match.tsx +218 -78
- package/src/Matches.tsx +45 -25
- package/src/Scripts.tsx +72 -44
- package/src/Transitioner.tsx +24 -16
- package/src/headContentUtils.tsx +210 -27
- package/src/link.tsx +66 -71
- package/src/not-found.tsx +41 -4
- package/src/router.ts +2 -1
- package/src/routerStores.ts +26 -0
- package/src/ssr/RouterClient.tsx +1 -1
- package/src/ssr/renderRouterToStream.tsx +2 -2
- package/src/ssr/renderRouterToString.tsx +1 -1
- package/src/useCanGoBack.ts +14 -2
- package/src/useLocation.tsx +32 -5
- package/src/useMatch.tsx +61 -21
- package/src/useRouterState.tsx +4 -2
package/dist/llms/rules/api.js
CHANGED
|
@@ -1841,7 +1841,6 @@ type RouterState = {
|
|
|
1841
1841
|
isLoading: boolean
|
|
1842
1842
|
isTransitioning: boolean
|
|
1843
1843
|
matches: Array<RouteMatch>
|
|
1844
|
-
pendingMatches: Array<RouteMatch>
|
|
1845
1844
|
location: ParsedLocation
|
|
1846
1845
|
resolvedLocation: ParsedLocation
|
|
1847
1846
|
}
|
|
@@ -1871,11 +1870,6 @@ The \`RouterState\` type contains all of the properties that are available on th
|
|
|
1871
1870
|
- Type: [\`Array<RouteMatch>\`](./RouteMatchType.md)
|
|
1872
1871
|
- An array of all of the route matches that have been resolved and are currently active.
|
|
1873
1872
|
|
|
1874
|
-
### \`pendingMatches\` property
|
|
1875
|
-
|
|
1876
|
-
- Type: [\`Array<RouteMatch>\`](./RouteMatchType.md)
|
|
1877
|
-
- An array of all of the route matches that are currently pending.
|
|
1878
|
-
|
|
1879
1873
|
### \`location\` property
|
|
1880
1874
|
|
|
1881
1875
|
- Type: [\`ParsedLocation\`](./ParsedLocationType.md)
|
|
@@ -3797,7 +3791,7 @@ function Component() {
|
|
|
3797
3791
|
The \`useChildMatches\` hook returns all of the child [\`RouteMatch\`](./RouteMatchType.md) objects from the closest match down to the leaf-most match. **It does not include the current match, which can be obtained using the \`useMatch\` hook.**
|
|
3798
3792
|
|
|
3799
3793
|
> [!IMPORTANT]
|
|
3800
|
-
> If the router has pending matches and they are showing their pending component fallbacks,
|
|
3794
|
+
> If the router has pending matches and they are showing their pending component fallbacks, pending matches are used instead of active matches.
|
|
3801
3795
|
|
|
3802
3796
|
## useChildMatches options
|
|
3803
3797
|
|
|
@@ -4406,7 +4400,7 @@ function Component() {
|
|
|
4406
4400
|
The \`useParentMatches\` hook returns all of the parent [\`RouteMatch\`](./RouteMatchType.md) objects from the root down to the immediate parent of the current match in context. **It does not include the current match, which can be obtained using the \`useMatch\` hook.**
|
|
4407
4401
|
|
|
4408
4402
|
> [!IMPORTANT]
|
|
4409
|
-
> If the router has pending matches and they are showing their pending component fallbacks,
|
|
4403
|
+
> If the router has pending matches and they are showing their pending component fallbacks, pending matches are used instead of active matches.
|
|
4410
4404
|
|
|
4411
4405
|
## useParentMatches options
|
|
4412
4406
|
|
|
@@ -4514,7 +4508,7 @@ function Component() {
|
|
|
4514
4508
|
The \`useRouterState\` method is a hook that returns the current internal state of the router. This hook is useful for accessing the current state of the router in a component.
|
|
4515
4509
|
|
|
4516
4510
|
> [!TIP]
|
|
4517
|
-
> If you want to access the current location or the current matches, you should try out the [\`useLocation\`](./useLocationHook.md) and [\`useMatches\`](./useMatchesHook.md) hooks first. These hooks are designed to be more ergonomic and easier to use than accessing the router state directly.
|
|
4511
|
+
> If you want to access the current location or the current matches, you should try out the [\`useLocation\`](./useLocationHook.md) and [\`useMatches\`](./useMatchesHook.md) hooks first. These hooks are designed to be more performant, more ergonomic and easier to use than accessing the router state directly.
|
|
4518
4512
|
|
|
4519
4513
|
## useRouterState options
|
|
4520
4514
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-router",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.168.1",
|
|
4
4
|
"description": "Modern and scalable routing for React applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -77,12 +77,12 @@
|
|
|
77
77
|
"node": ">=20.19"
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
|
-
"@tanstack/react-store": "^0.9.
|
|
80
|
+
"@tanstack/react-store": "^0.9.2",
|
|
81
81
|
"isbot": "^5.1.22",
|
|
82
82
|
"tiny-invariant": "^1.3.3",
|
|
83
83
|
"tiny-warning": "^1.0.3",
|
|
84
84
|
"@tanstack/history": "1.161.6",
|
|
85
|
-
"@tanstack/router-core": "1.
|
|
85
|
+
"@tanstack/router-core": "1.168.1"
|
|
86
86
|
},
|
|
87
87
|
"devDependencies": {
|
|
88
88
|
"@testing-library/jest-dom": "^6.6.3",
|
package/src/Match.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
|
+
import { useStore } from '@tanstack/react-store'
|
|
2
3
|
import invariant from 'tiny-invariant'
|
|
3
4
|
import warning from 'tiny-warning'
|
|
4
5
|
import {
|
|
@@ -10,7 +11,6 @@ import {
|
|
|
10
11
|
} from '@tanstack/router-core'
|
|
11
12
|
import { isServer } from '@tanstack/router-core/isServer'
|
|
12
13
|
import { CatchBoundary, ErrorComponent } from './CatchBoundary'
|
|
13
|
-
import { useRouterState } from './useRouterState'
|
|
14
14
|
import { useRouter } from './useRouter'
|
|
15
15
|
import { CatchNotFound } from './not-found'
|
|
16
16
|
import { matchContext } from './matchContext'
|
|
@@ -30,25 +30,88 @@ export const Match = React.memo(function MatchImpl({
|
|
|
30
30
|
matchId: string
|
|
31
31
|
}) {
|
|
32
32
|
const router = useRouter()
|
|
33
|
-
const matchState = useRouterState({
|
|
34
|
-
select: (s) => {
|
|
35
|
-
const matchIndex = s.matches.findIndex((d) => d.id === matchId)
|
|
36
|
-
const match = s.matches[matchIndex]
|
|
37
|
-
invariant(
|
|
38
|
-
match,
|
|
39
|
-
`Could not find match for matchId "${matchId}". Please file an issue!`,
|
|
40
|
-
)
|
|
41
|
-
return {
|
|
42
|
-
routeId: match.routeId,
|
|
43
|
-
ssr: match.ssr,
|
|
44
|
-
_displayPending: match._displayPending,
|
|
45
|
-
resetKey: s.loadedAt,
|
|
46
|
-
parentRouteId: s.matches[matchIndex - 1]?.routeId as string,
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
structuralSharing: true as any,
|
|
50
|
-
})
|
|
51
33
|
|
|
34
|
+
if (isServer ?? router.isServer) {
|
|
35
|
+
const match = router.stores.activeMatchStoresById.get(matchId)?.state
|
|
36
|
+
invariant(
|
|
37
|
+
match,
|
|
38
|
+
`Could not find match for matchId "${matchId}". Please file an issue!`,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
const routeId = match.routeId as string
|
|
42
|
+
const parentRouteId = (router.routesById[routeId] as AnyRoute).parentRoute
|
|
43
|
+
?.id
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<MatchView
|
|
47
|
+
router={router}
|
|
48
|
+
matchId={matchId}
|
|
49
|
+
resetKey={router.stores.loadedAt.state}
|
|
50
|
+
matchState={{
|
|
51
|
+
routeId,
|
|
52
|
+
ssr: match.ssr,
|
|
53
|
+
_displayPending: match._displayPending,
|
|
54
|
+
parentRouteId,
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Subscribe directly to the match store from the pool.
|
|
61
|
+
// The matchId prop is stable for this component's lifetime (set by Outlet),
|
|
62
|
+
// and reconcileMatchPool reuses stores for the same matchId.
|
|
63
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
64
|
+
const matchStore = router.stores.activeMatchStoresById.get(matchId)
|
|
65
|
+
invariant(
|
|
66
|
+
matchStore,
|
|
67
|
+
`Could not find match for matchId "${matchId}". Please file an issue!`,
|
|
68
|
+
)
|
|
69
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
70
|
+
const resetKey = useStore(router.stores.loadedAt, (loadedAt) => loadedAt)
|
|
71
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
72
|
+
const match = useStore(matchStore, (value) => value)
|
|
73
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
74
|
+
const matchState = React.useMemo(() => {
|
|
75
|
+
const routeId = match.routeId as string
|
|
76
|
+
const parentRouteId = (router.routesById[routeId] as AnyRoute).parentRoute
|
|
77
|
+
?.id
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
routeId,
|
|
81
|
+
ssr: match.ssr,
|
|
82
|
+
_displayPending: match._displayPending,
|
|
83
|
+
parentRouteId: parentRouteId as string | undefined,
|
|
84
|
+
} satisfies MatchViewState
|
|
85
|
+
}, [match._displayPending, match.routeId, match.ssr, router.routesById])
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<MatchView
|
|
89
|
+
router={router}
|
|
90
|
+
matchId={matchId}
|
|
91
|
+
resetKey={resetKey}
|
|
92
|
+
matchState={matchState}
|
|
93
|
+
/>
|
|
94
|
+
)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
type MatchViewState = {
|
|
98
|
+
routeId: string
|
|
99
|
+
ssr: boolean | 'data-only' | undefined
|
|
100
|
+
_displayPending: boolean | undefined
|
|
101
|
+
parentRouteId: string | undefined
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function MatchView({
|
|
105
|
+
router,
|
|
106
|
+
matchId,
|
|
107
|
+
resetKey,
|
|
108
|
+
matchState,
|
|
109
|
+
}: {
|
|
110
|
+
router: ReturnType<typeof useRouter>
|
|
111
|
+
matchId: string
|
|
112
|
+
resetKey: number
|
|
113
|
+
matchState: MatchViewState
|
|
114
|
+
}) {
|
|
52
115
|
const route: AnyRoute = router.routesById[matchState.routeId]
|
|
53
116
|
|
|
54
117
|
const PendingComponent =
|
|
@@ -94,7 +157,7 @@ export const Match = React.memo(function MatchImpl({
|
|
|
94
157
|
<matchContext.Provider value={matchId}>
|
|
95
158
|
<ResolvedSuspenseBoundary fallback={pendingElement}>
|
|
96
159
|
<ResolvedCatchBoundary
|
|
97
|
-
getResetKey={() =>
|
|
160
|
+
getResetKey={() => resetKey}
|
|
98
161
|
errorComponent={routeErrorComponent || ErrorComponent}
|
|
99
162
|
onCatch={(error, errorInfo) => {
|
|
100
163
|
// Forward not found errors (we don't want to show the error component for these)
|
|
@@ -137,7 +200,7 @@ export const Match = React.memo(function MatchImpl({
|
|
|
137
200
|
) : null}
|
|
138
201
|
</ShellComponent>
|
|
139
202
|
)
|
|
140
|
-
}
|
|
203
|
+
}
|
|
141
204
|
|
|
142
205
|
// On Rendered can't happen above the root layout because it actually
|
|
143
206
|
// renders a dummy dom element to track the rendered state of the app.
|
|
@@ -165,7 +228,10 @@ function OnRendered() {
|
|
|
165
228
|
) {
|
|
166
229
|
router.emit({
|
|
167
230
|
type: 'onRendered',
|
|
168
|
-
...getLocationChangeInfo(
|
|
231
|
+
...getLocationChangeInfo(
|
|
232
|
+
router.stores.location.state,
|
|
233
|
+
router.stores.resolvedLocation.state,
|
|
234
|
+
),
|
|
169
235
|
})
|
|
170
236
|
prevLocationRef.current = router.latestLocation
|
|
171
237
|
}
|
|
@@ -181,39 +247,101 @@ export const MatchInner = React.memo(function MatchInnerImpl({
|
|
|
181
247
|
}): any {
|
|
182
248
|
const router = useRouter()
|
|
183
249
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
(router.routesById[routeId] as AnyRoute).options.remountDeps ??
|
|
191
|
-
router.options.defaultRemountDeps
|
|
192
|
-
const remountDeps = remountFn?.({
|
|
193
|
-
routeId,
|
|
194
|
-
loaderDeps: match.loaderDeps,
|
|
195
|
-
params: match._strictParams,
|
|
196
|
-
search: match._strictSearch,
|
|
197
|
-
})
|
|
198
|
-
const key = remountDeps ? JSON.stringify(remountDeps) : undefined
|
|
199
|
-
|
|
200
|
-
return {
|
|
201
|
-
key,
|
|
202
|
-
routeId,
|
|
203
|
-
match: {
|
|
204
|
-
id: match.id,
|
|
205
|
-
status: match.status,
|
|
206
|
-
error: match.error,
|
|
207
|
-
_forcePending: match._forcePending,
|
|
208
|
-
_displayPending: match._displayPending,
|
|
209
|
-
},
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
structuralSharing: true as any,
|
|
213
|
-
})
|
|
250
|
+
if (isServer ?? router.isServer) {
|
|
251
|
+
const match = router.stores.activeMatchStoresById.get(matchId)?.state
|
|
252
|
+
invariant(
|
|
253
|
+
match,
|
|
254
|
+
`Could not find match for matchId "${matchId}". Please file an issue!`,
|
|
255
|
+
)
|
|
214
256
|
|
|
215
|
-
|
|
257
|
+
const routeId = match.routeId as string
|
|
258
|
+
const route = router.routesById[routeId] as AnyRoute
|
|
259
|
+
const remountFn =
|
|
260
|
+
(router.routesById[routeId] as AnyRoute).options.remountDeps ??
|
|
261
|
+
router.options.defaultRemountDeps
|
|
262
|
+
const remountDeps = remountFn?.({
|
|
263
|
+
routeId,
|
|
264
|
+
loaderDeps: match.loaderDeps,
|
|
265
|
+
params: match._strictParams,
|
|
266
|
+
search: match._strictSearch,
|
|
267
|
+
})
|
|
268
|
+
const key = remountDeps ? JSON.stringify(remountDeps) : undefined
|
|
269
|
+
const Comp = route.options.component ?? router.options.defaultComponent
|
|
270
|
+
const out = Comp ? <Comp key={key} /> : <Outlet />
|
|
271
|
+
|
|
272
|
+
if (match._displayPending) {
|
|
273
|
+
throw router.getMatch(match.id)?._nonReactive.displayPendingPromise
|
|
274
|
+
}
|
|
216
275
|
|
|
276
|
+
if (match._forcePending) {
|
|
277
|
+
throw router.getMatch(match.id)?._nonReactive.minPendingPromise
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (match.status === 'pending') {
|
|
281
|
+
throw router.getMatch(match.id)?._nonReactive.loadPromise
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (match.status === 'notFound') {
|
|
285
|
+
invariant(isNotFound(match.error), 'Expected a notFound error')
|
|
286
|
+
return renderRouteNotFound(router, route, match.error)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (match.status === 'redirected') {
|
|
290
|
+
invariant(isRedirect(match.error), 'Expected a redirect error')
|
|
291
|
+
throw router.getMatch(match.id)?._nonReactive.loadPromise
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (match.status === 'error') {
|
|
295
|
+
const RouteErrorComponent =
|
|
296
|
+
(route.options.errorComponent ??
|
|
297
|
+
router.options.defaultErrorComponent) ||
|
|
298
|
+
ErrorComponent
|
|
299
|
+
return (
|
|
300
|
+
<RouteErrorComponent
|
|
301
|
+
error={match.error as any}
|
|
302
|
+
reset={undefined as any}
|
|
303
|
+
info={{
|
|
304
|
+
componentStack: '',
|
|
305
|
+
}}
|
|
306
|
+
/>
|
|
307
|
+
)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return out
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
314
|
+
const matchStore = router.stores.activeMatchStoresById.get(matchId)
|
|
315
|
+
invariant(
|
|
316
|
+
matchStore,
|
|
317
|
+
`Could not find match for matchId "${matchId}". Please file an issue!`,
|
|
318
|
+
)
|
|
319
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
320
|
+
const match = useStore(matchStore, (value) => value)
|
|
321
|
+
const routeId = match.routeId as string
|
|
322
|
+
const route = router.routesById[routeId] as AnyRoute
|
|
323
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
324
|
+
const key = React.useMemo(() => {
|
|
325
|
+
const remountFn =
|
|
326
|
+
(router.routesById[routeId] as AnyRoute).options.remountDeps ??
|
|
327
|
+
router.options.defaultRemountDeps
|
|
328
|
+
const remountDeps = remountFn?.({
|
|
329
|
+
routeId,
|
|
330
|
+
loaderDeps: match.loaderDeps,
|
|
331
|
+
params: match._strictParams,
|
|
332
|
+
search: match._strictSearch,
|
|
333
|
+
})
|
|
334
|
+
return remountDeps ? JSON.stringify(remountDeps) : undefined
|
|
335
|
+
}, [
|
|
336
|
+
routeId,
|
|
337
|
+
match.loaderDeps,
|
|
338
|
+
match._strictParams,
|
|
339
|
+
match._strictSearch,
|
|
340
|
+
router.options.defaultRemountDeps,
|
|
341
|
+
router.routesById,
|
|
342
|
+
])
|
|
343
|
+
|
|
344
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
217
345
|
const out = React.useMemo(() => {
|
|
218
346
|
const Comp = route.options.component ?? router.options.defaultComponent
|
|
219
347
|
if (Comp) {
|
|
@@ -309,37 +437,49 @@ export const MatchInner = React.memo(function MatchInnerImpl({
|
|
|
309
437
|
export const Outlet = React.memo(function OutletImpl() {
|
|
310
438
|
const router = useRouter()
|
|
311
439
|
const matchId = React.useContext(matchContext)
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
440
|
+
|
|
441
|
+
let routeId: string | undefined
|
|
442
|
+
let parentGlobalNotFound = false
|
|
443
|
+
let childMatchId: string | undefined
|
|
444
|
+
|
|
445
|
+
if (isServer ?? router.isServer) {
|
|
446
|
+
const matches = router.stores.activeMatchesSnapshot.state
|
|
447
|
+
const parentIndex = matchId
|
|
448
|
+
? matches.findIndex((match) => match.id === matchId)
|
|
449
|
+
: -1
|
|
450
|
+
const parentMatch = parentIndex >= 0 ? matches[parentIndex] : undefined
|
|
451
|
+
routeId = parentMatch?.routeId as string | undefined
|
|
452
|
+
parentGlobalNotFound = parentMatch?.globalNotFound ?? false
|
|
453
|
+
childMatchId =
|
|
454
|
+
parentIndex >= 0 ? (matches[parentIndex + 1]?.id as string) : undefined
|
|
455
|
+
} else {
|
|
456
|
+
// Subscribe directly to the match store from the pool instead of
|
|
457
|
+
// the two-level byId → matchStore pattern.
|
|
458
|
+
const parentMatchStore = matchId
|
|
459
|
+
? router.stores.activeMatchStoresById.get(matchId)
|
|
460
|
+
: undefined
|
|
461
|
+
|
|
462
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
463
|
+
;[routeId, parentGlobalNotFound] = useStore(parentMatchStore, (match) => [
|
|
464
|
+
match?.routeId as string | undefined,
|
|
465
|
+
match?.globalNotFound ?? false,
|
|
466
|
+
])
|
|
467
|
+
|
|
468
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
469
|
+
childMatchId = useStore(router.stores.matchesId, (ids) => {
|
|
470
|
+
const index = ids.findIndex((id) => id === matchId)
|
|
471
|
+
return ids[index + 1]
|
|
472
|
+
})
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const route = routeId ? router.routesById[routeId] : undefined
|
|
337
476
|
|
|
338
477
|
const pendingElement = router.options.defaultPendingComponent ? (
|
|
339
478
|
<router.options.defaultPendingComponent />
|
|
340
479
|
) : null
|
|
341
480
|
|
|
342
481
|
if (parentGlobalNotFound) {
|
|
482
|
+
invariant(route, 'Could not resolve route for Outlet render')
|
|
343
483
|
return renderRouteNotFound(router, route, undefined)
|
|
344
484
|
}
|
|
345
485
|
|
package/src/Matches.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import warning from 'tiny-warning'
|
|
3
|
-
import {
|
|
3
|
+
import { useStore } from '@tanstack/react-store'
|
|
4
|
+
import { replaceEqualDeep, rootRouteId } from '@tanstack/router-core'
|
|
4
5
|
import { isServer } from '@tanstack/router-core/isServer'
|
|
5
6
|
import { CatchBoundary, ErrorComponent } from './CatchBoundary'
|
|
6
|
-
import { useRouterState } from './useRouterState'
|
|
7
7
|
import { useRouter } from './useRouter'
|
|
8
8
|
import { Transitioner } from './Transitioner'
|
|
9
9
|
import { matchContext } from './matchContext'
|
|
@@ -28,7 +28,6 @@ import type {
|
|
|
28
28
|
ResolveRelativePath,
|
|
29
29
|
ResolveRoute,
|
|
30
30
|
RouteByPath,
|
|
31
|
-
RouterState,
|
|
32
31
|
ToSubOptionsProps,
|
|
33
32
|
} from '@tanstack/router-core'
|
|
34
33
|
|
|
@@ -78,15 +77,15 @@ export function Matches() {
|
|
|
78
77
|
|
|
79
78
|
function MatchesInner() {
|
|
80
79
|
const router = useRouter()
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
const _isServer = isServer ?? router.isServer
|
|
81
|
+
const matchId = _isServer
|
|
82
|
+
? router.stores.firstMatchId.state
|
|
83
|
+
: // eslint-disable-next-line react-hooks/rules-of-hooks
|
|
84
|
+
useStore(router.stores.firstMatchId, (id) => id)
|
|
85
|
+
const resetKey = _isServer
|
|
86
|
+
? router.stores.loadedAt.state
|
|
87
|
+
: // eslint-disable-next-line react-hooks/rules-of-hooks
|
|
88
|
+
useStore(router.stores.loadedAt, (loadedAt) => loadedAt)
|
|
90
89
|
|
|
91
90
|
const matchComponent = matchId ? <Match matchId={matchId} /> : null
|
|
92
91
|
|
|
@@ -144,10 +143,10 @@ export type UseMatchRouteOptions<
|
|
|
144
143
|
export function useMatchRoute<TRouter extends AnyRouter = RegisteredRouter>() {
|
|
145
144
|
const router = useRouter()
|
|
146
145
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
146
|
+
if (!(isServer ?? router.isServer)) {
|
|
147
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
148
|
+
useStore(router.stores.matchRouteReactivity, (d) => d)
|
|
149
|
+
}
|
|
151
150
|
|
|
152
151
|
return React.useCallback(
|
|
153
152
|
<
|
|
@@ -238,15 +237,36 @@ export function useMatches<
|
|
|
238
237
|
opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &
|
|
239
238
|
StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
|
|
240
239
|
): UseMatchesResult<TRouter, TSelected> {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
240
|
+
const router = useRouter<TRouter>()
|
|
241
|
+
const previousResult =
|
|
242
|
+
React.useRef<ValidateSelected<TRouter, TSelected, TStructuralSharing>>(
|
|
243
|
+
undefined,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
if (isServer ?? router.isServer) {
|
|
247
|
+
const matches = router.stores.activeMatchesSnapshot.state as Array<
|
|
248
|
+
MakeRouteMatchUnion<TRouter>
|
|
249
|
+
>
|
|
250
|
+
return (opts?.select ? opts.select(matches) : matches) as UseMatchesResult<
|
|
251
|
+
TRouter,
|
|
252
|
+
TSelected
|
|
253
|
+
>
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
257
|
+
return useStore(router.stores.activeMatchesSnapshot, (matches) => {
|
|
258
|
+
const selected = opts?.select
|
|
259
|
+
? opts.select(matches as Array<MakeRouteMatchUnion<TRouter>>)
|
|
260
|
+
: (matches as any)
|
|
261
|
+
|
|
262
|
+
if (opts?.structuralSharing ?? router.options.defaultStructuralSharing) {
|
|
263
|
+
const shared = replaceEqualDeep(previousResult.current, selected)
|
|
264
|
+
previousResult.current = shared
|
|
265
|
+
return shared
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return selected
|
|
269
|
+
}) as UseMatchesResult<TRouter, TSelected>
|
|
250
270
|
}
|
|
251
271
|
|
|
252
272
|
/**
|
package/src/Scripts.tsx
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { useStore } from '@tanstack/react-store'
|
|
2
|
+
import { deepEqual } from '@tanstack/router-core'
|
|
3
|
+
import { isServer } from '@tanstack/router-core/isServer'
|
|
1
4
|
import { Asset } from './Asset'
|
|
2
|
-
import { useRouterState } from './useRouterState'
|
|
3
5
|
import { useRouter } from './useRouter'
|
|
4
6
|
import type { RouterManagedTag } from '@tanstack/router-core'
|
|
5
7
|
|
|
@@ -10,54 +12,80 @@ import type { RouterManagedTag } from '@tanstack/router-core'
|
|
|
10
12
|
export const Scripts = () => {
|
|
11
13
|
const router = useRouter()
|
|
12
14
|
const nonce = router.options.ssr?.nonce
|
|
13
|
-
const assetScripts = useRouterState({
|
|
14
|
-
select: (state) => {
|
|
15
|
-
const assetScripts: Array<RouterManagedTag> = []
|
|
16
|
-
const manifest = router.ssr?.manifest
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
const getAssetScripts = (matches: Array<any>) => {
|
|
17
|
+
const assetScripts: Array<RouterManagedTag> = []
|
|
18
|
+
const manifest = router.ssr?.manifest
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
manifest.routes[route.id]?.assets
|
|
26
|
-
?.filter((d) => d.tag === 'script')
|
|
27
|
-
.forEach((asset) => {
|
|
28
|
-
assetScripts.push({
|
|
29
|
-
tag: 'script',
|
|
30
|
-
attrs: { ...asset.attrs, nonce },
|
|
31
|
-
children: asset.children,
|
|
32
|
-
} as any)
|
|
33
|
-
}),
|
|
34
|
-
)
|
|
20
|
+
if (!manifest) {
|
|
21
|
+
return []
|
|
22
|
+
}
|
|
35
23
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
24
|
+
matches
|
|
25
|
+
.map((match) => router.looseRoutesById[match.routeId]!)
|
|
26
|
+
.forEach((route) =>
|
|
27
|
+
manifest.routes[route.id]?.assets
|
|
28
|
+
?.filter((d) => d.tag === 'script')
|
|
29
|
+
.forEach((asset) => {
|
|
30
|
+
assetScripts.push({
|
|
31
|
+
tag: 'script',
|
|
32
|
+
attrs: { ...asset.attrs, nonce },
|
|
33
|
+
children: asset.children,
|
|
34
|
+
} as any)
|
|
35
|
+
}),
|
|
36
|
+
)
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
38
|
+
return assetScripts
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const getScripts = (matches: Array<any>): Array<RouterManagedTag> =>
|
|
42
|
+
(
|
|
43
|
+
matches
|
|
44
|
+
.map((match) => match.scripts!)
|
|
45
|
+
.flat(1)
|
|
46
|
+
.filter(Boolean) as Array<RouterManagedTag>
|
|
47
|
+
).map(
|
|
48
|
+
({ children, ...script }) =>
|
|
49
|
+
({
|
|
50
|
+
tag: 'script',
|
|
51
|
+
attrs: {
|
|
52
|
+
...script,
|
|
53
|
+
suppressHydrationWarning: true,
|
|
54
|
+
nonce,
|
|
55
|
+
},
|
|
56
|
+
children,
|
|
57
|
+
}) satisfies RouterManagedTag,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if (isServer ?? router.isServer) {
|
|
61
|
+
const assetScripts = getAssetScripts(
|
|
62
|
+
router.stores.activeMatchesSnapshot.state,
|
|
63
|
+
)
|
|
64
|
+
const scripts = getScripts(router.stores.activeMatchesSnapshot.state)
|
|
65
|
+
return renderScripts(router, scripts, assetScripts)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
|
|
69
|
+
const assetScripts = useStore(
|
|
70
|
+
router.stores.activeMatchesSnapshot,
|
|
71
|
+
getAssetScripts,
|
|
72
|
+
deepEqual,
|
|
73
|
+
)
|
|
74
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
|
|
75
|
+
const scripts = useStore(
|
|
76
|
+
router.stores.activeMatchesSnapshot,
|
|
77
|
+
getScripts,
|
|
78
|
+
deepEqual,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return renderScripts(router, scripts, assetScripts)
|
|
82
|
+
}
|
|
60
83
|
|
|
84
|
+
function renderScripts(
|
|
85
|
+
router: ReturnType<typeof useRouter>,
|
|
86
|
+
scripts: Array<RouterManagedTag>,
|
|
87
|
+
assetScripts: Array<RouterManagedTag>,
|
|
88
|
+
) {
|
|
61
89
|
let serverBufferedScript: RouterManagedTag | undefined = undefined
|
|
62
90
|
|
|
63
91
|
if (router.serverSsr) {
|