@tanstack/react-router 1.78.3 → 1.81.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/Match.cjs +10 -11
- package/dist/cjs/Match.cjs.map +1 -1
- package/dist/cjs/Matches.cjs +8 -4
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/Matches.d.cts +8 -9
- package/dist/cjs/RouterProvider.cjs.map +1 -1
- package/dist/cjs/RouterProvider.d.cts +3 -3
- package/dist/cjs/Transitioner.cjs +9 -8
- package/dist/cjs/Transitioner.cjs.map +1 -1
- package/dist/cjs/fileRoute.cjs +15 -3
- package/dist/cjs/fileRoute.cjs.map +1 -1
- package/dist/cjs/fileRoute.d.cts +12 -19
- package/dist/cjs/link.cjs +4 -1
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/link.d.cts +1 -1
- package/dist/cjs/path.cjs +12 -2
- package/dist/cjs/path.cjs.map +1 -1
- package/dist/cjs/path.d.cts +2 -1
- package/dist/cjs/route.cjs +33 -9
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +20 -38
- package/dist/cjs/router.cjs +15 -4
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +25 -11
- package/dist/cjs/routerContext.cjs.map +1 -1
- package/dist/cjs/routerContext.d.cts +1 -1
- package/dist/cjs/structuralSharing.d.cts +12 -0
- package/dist/cjs/useLoaderData.cjs +4 -2
- package/dist/cjs/useLoaderData.cjs.map +1 -1
- package/dist/cjs/useLoaderData.d.cts +12 -9
- package/dist/cjs/useLoaderDeps.cjs +3 -2
- package/dist/cjs/useLoaderDeps.cjs.map +1 -1
- package/dist/cjs/useLoaderDeps.d.cts +12 -8
- package/dist/cjs/useLocation.cjs.map +1 -1
- package/dist/cjs/useLocation.d.cts +6 -3
- package/dist/cjs/useMatch.cjs +2 -1
- package/dist/cjs/useMatch.cjs.map +1 -1
- package/dist/cjs/useMatch.d.cts +10 -8
- package/dist/cjs/useParams.cjs +3 -1
- package/dist/cjs/useParams.cjs.map +1 -1
- package/dist/cjs/useParams.d.cts +12 -8
- package/dist/cjs/useRouteContext.cjs.map +1 -1
- package/dist/cjs/useRouteContext.d.cts +11 -8
- package/dist/cjs/useRouterState.cjs +18 -1
- package/dist/cjs/useRouterState.cjs.map +1 -1
- package/dist/cjs/useRouterState.d.cts +6 -3
- package/dist/cjs/useSearch.cjs +3 -1
- package/dist/cjs/useSearch.cjs.map +1 -1
- package/dist/cjs/useSearch.d.cts +12 -8
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +10 -4
- package/dist/esm/Match.js +10 -11
- package/dist/esm/Match.js.map +1 -1
- package/dist/esm/Matches.d.ts +8 -9
- package/dist/esm/Matches.js +8 -4
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/RouterProvider.d.ts +3 -3
- package/dist/esm/RouterProvider.js.map +1 -1
- package/dist/esm/Transitioner.js +10 -9
- package/dist/esm/Transitioner.js.map +1 -1
- package/dist/esm/fileRoute.d.ts +12 -19
- package/dist/esm/fileRoute.js +15 -3
- package/dist/esm/fileRoute.js.map +1 -1
- package/dist/esm/link.d.ts +1 -1
- package/dist/esm/link.js +4 -1
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/path.d.ts +2 -1
- package/dist/esm/path.js +12 -2
- package/dist/esm/path.js.map +1 -1
- package/dist/esm/route.d.ts +20 -38
- package/dist/esm/route.js +33 -9
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +25 -11
- package/dist/esm/router.js +15 -4
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/routerContext.d.ts +1 -1
- package/dist/esm/routerContext.js.map +1 -1
- package/dist/esm/structuralSharing.d.ts +12 -0
- package/dist/esm/useLoaderData.d.ts +12 -9
- package/dist/esm/useLoaderData.js +4 -2
- package/dist/esm/useLoaderData.js.map +1 -1
- package/dist/esm/useLoaderDeps.d.ts +12 -8
- package/dist/esm/useLoaderDeps.js +3 -2
- package/dist/esm/useLoaderDeps.js.map +1 -1
- package/dist/esm/useLocation.d.ts +6 -3
- package/dist/esm/useLocation.js.map +1 -1
- package/dist/esm/useMatch.d.ts +10 -8
- package/dist/esm/useMatch.js +2 -1
- package/dist/esm/useMatch.js.map +1 -1
- package/dist/esm/useParams.d.ts +12 -8
- package/dist/esm/useParams.js +3 -1
- package/dist/esm/useParams.js.map +1 -1
- package/dist/esm/useRouteContext.d.ts +11 -8
- package/dist/esm/useRouteContext.js.map +1 -1
- package/dist/esm/useRouterState.d.ts +6 -3
- package/dist/esm/useRouterState.js +18 -1
- package/dist/esm/useRouterState.js.map +1 -1
- package/dist/esm/useSearch.d.ts +12 -8
- package/dist/esm/useSearch.js +3 -1
- package/dist/esm/useSearch.js.map +1 -1
- package/dist/esm/utils.d.ts +10 -4
- package/dist/esm/utils.js.map +1 -1
- package/package.json +3 -3
- package/src/Match.tsx +6 -7
- package/src/Matches.tsx +52 -24
- package/src/RouterProvider.tsx +4 -1
- package/src/Transitioner.tsx +9 -10
- package/src/fileRoute.ts +29 -29
- package/src/link.tsx +6 -3
- package/src/path.ts +16 -1
- package/src/route.ts +57 -101
- package/src/router.ts +60 -4
- package/src/routerContext.tsx +1 -1
- package/src/structuralSharing.ts +49 -0
- package/src/useLoaderData.tsx +76 -28
- package/src/useLoaderDeps.tsx +55 -20
- package/src/useLocation.tsx +30 -8
- package/src/useMatch.tsx +71 -21
- package/src/useParams.tsx +70 -21
- package/src/useRouteContext.ts +45 -23
- package/src/useRouterState.tsx +45 -6
- package/src/useSearch.tsx +69 -20
- package/src/utils.ts +16 -3
package/src/Match.tsx
CHANGED
|
@@ -120,9 +120,10 @@ export const MatchInner = React.memo(function MatchInnerImpl({
|
|
|
120
120
|
return {
|
|
121
121
|
routeId,
|
|
122
122
|
matchIndex,
|
|
123
|
-
match: pick(match, ['id', 'status', 'error'
|
|
123
|
+
match: pick(match, ['id', 'status', 'error']),
|
|
124
124
|
}
|
|
125
125
|
},
|
|
126
|
+
structuralSharing: true as any,
|
|
126
127
|
})
|
|
127
128
|
|
|
128
129
|
const route = router.routesById[routeId]!
|
|
@@ -180,7 +181,7 @@ export const MatchInner = React.memo(function MatchInnerImpl({
|
|
|
180
181
|
// false,
|
|
181
182
|
// 'Tried to render a redirected route match! This is a weird circumstance, please file an issue!',
|
|
182
183
|
// )
|
|
183
|
-
throw match.loadPromise
|
|
184
|
+
throw router.getMatch(match.id)?.loadPromise
|
|
184
185
|
}
|
|
185
186
|
|
|
186
187
|
if (match.status === 'error') {
|
|
@@ -237,7 +238,7 @@ export const MatchInner = React.memo(function MatchInnerImpl({
|
|
|
237
238
|
}, pendingMinMs)
|
|
238
239
|
}
|
|
239
240
|
}
|
|
240
|
-
throw match.loadPromise
|
|
241
|
+
throw router.getMatch(match.id)?.loadPromise
|
|
241
242
|
}
|
|
242
243
|
|
|
243
244
|
return (
|
|
@@ -259,7 +260,7 @@ export const Outlet = React.memo(function OutletImpl() {
|
|
|
259
260
|
|
|
260
261
|
const route = router.routesById[routeId]!
|
|
261
262
|
|
|
262
|
-
const
|
|
263
|
+
const parentGlobalNotFound = useRouterState({
|
|
263
264
|
select: (s) => {
|
|
264
265
|
const matches = s.matches
|
|
265
266
|
const parentMatch = matches.find((d) => d.id === matchId)
|
|
@@ -267,9 +268,7 @@ export const Outlet = React.memo(function OutletImpl() {
|
|
|
267
268
|
parentMatch,
|
|
268
269
|
`Could not find parent match for matchId "${matchId}"`,
|
|
269
270
|
)
|
|
270
|
-
return
|
|
271
|
-
parentGlobalNotFound: parentMatch.globalNotFound,
|
|
272
|
-
}
|
|
271
|
+
return parentMatch.globalNotFound
|
|
273
272
|
},
|
|
274
273
|
})
|
|
275
274
|
|
package/src/Matches.tsx
CHANGED
|
@@ -7,8 +7,12 @@ import { Transitioner } from './Transitioner'
|
|
|
7
7
|
import { matchContext } from './matchContext'
|
|
8
8
|
import { Match } from './Match'
|
|
9
9
|
import { SafeFragment } from './SafeFragment'
|
|
10
|
+
import type {
|
|
11
|
+
StructuralSharingOption,
|
|
12
|
+
ValidateSelected,
|
|
13
|
+
} from './structuralSharing'
|
|
10
14
|
import type { AnyRoute, ReactNode, StaticDataRouteOption } from './route'
|
|
11
|
-
import type { AnyRouter, RegisteredRouter } from './router'
|
|
15
|
+
import type { AnyRouter, RegisteredRouter, RouterState } from './router'
|
|
12
16
|
import type { ResolveRelativePath, ToOptions } from './link'
|
|
13
17
|
import type {
|
|
14
18
|
AllContext,
|
|
@@ -288,6 +292,7 @@ export function useMatchRoute<TRouter extends AnyRouter = RegisteredRouter>() {
|
|
|
288
292
|
|
|
289
293
|
useRouterState({
|
|
290
294
|
select: (s) => [s.location.href, s.resolvedLocation.href, s.status],
|
|
295
|
+
structuralSharing: true as any,
|
|
291
296
|
})
|
|
292
297
|
|
|
293
298
|
return React.useCallback(
|
|
@@ -369,56 +374,79 @@ export type MakeRouteMatchUnion<
|
|
|
369
374
|
>
|
|
370
375
|
: never
|
|
371
376
|
|
|
377
|
+
export interface UseMatchesBaseOptions<
|
|
378
|
+
TRouter extends AnyRouter,
|
|
379
|
+
TSelected,
|
|
380
|
+
TStructuralSharing,
|
|
381
|
+
> {
|
|
382
|
+
select?: (
|
|
383
|
+
matches: Array<MakeRouteMatchUnion<TRouter>>,
|
|
384
|
+
) => ValidateSelected<TRouter, TSelected, TStructuralSharing>
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export type UseMatchesResult<
|
|
388
|
+
TRouter extends AnyRouter,
|
|
389
|
+
TSelected,
|
|
390
|
+
> = unknown extends TSelected ? Array<MakeRouteMatchUnion<TRouter>> : TSelected
|
|
391
|
+
|
|
372
392
|
export function useMatches<
|
|
373
393
|
TRouter extends AnyRouter = RegisteredRouter,
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
>(
|
|
394
|
+
TSelected = unknown,
|
|
395
|
+
TStructuralSharing extends boolean = boolean,
|
|
396
|
+
>(
|
|
397
|
+
opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &
|
|
398
|
+
StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
|
|
399
|
+
): UseMatchesResult<TRouter, TSelected> {
|
|
377
400
|
return useRouterState({
|
|
378
|
-
select: (state) => {
|
|
401
|
+
select: (state: RouterState<TRouter['routeTree']>) => {
|
|
379
402
|
const matches = state.matches
|
|
380
403
|
return opts?.select
|
|
381
|
-
? opts.select(matches as Array<
|
|
382
|
-
:
|
|
404
|
+
? opts.select(matches as Array<MakeRouteMatchUnion<TRouter>>)
|
|
405
|
+
: matches
|
|
383
406
|
},
|
|
384
|
-
|
|
407
|
+
structuralSharing: opts?.structuralSharing,
|
|
408
|
+
} as any) as UseMatchesResult<TRouter, TSelected>
|
|
385
409
|
}
|
|
386
410
|
|
|
387
411
|
export function useParentMatches<
|
|
388
412
|
TRouter extends AnyRouter = RegisteredRouter,
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
>(
|
|
413
|
+
TSelected = unknown,
|
|
414
|
+
TStructuralSharing extends boolean = boolean,
|
|
415
|
+
>(
|
|
416
|
+
opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &
|
|
417
|
+
StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
|
|
418
|
+
): UseMatchesResult<TRouter, TSelected> {
|
|
392
419
|
const contextMatchId = React.useContext(matchContext)
|
|
393
420
|
|
|
394
421
|
return useMatches({
|
|
395
|
-
select: (matches) => {
|
|
422
|
+
select: (matches: Array<MakeRouteMatchUnion<TRouter>>) => {
|
|
396
423
|
matches = matches.slice(
|
|
397
424
|
0,
|
|
398
425
|
matches.findIndex((d) => d.id === contextMatchId),
|
|
399
426
|
)
|
|
400
|
-
return opts?.select
|
|
401
|
-
? opts.select(matches as Array<TRouteMatch>)
|
|
402
|
-
: (matches as T)
|
|
427
|
+
return opts?.select ? opts.select(matches) : matches
|
|
403
428
|
},
|
|
404
|
-
|
|
429
|
+
structuralSharing: opts?.structuralSharing,
|
|
430
|
+
} as any)
|
|
405
431
|
}
|
|
406
432
|
|
|
407
433
|
export function useChildMatches<
|
|
408
434
|
TRouter extends AnyRouter = RegisteredRouter,
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
>(
|
|
435
|
+
TSelected = unknown,
|
|
436
|
+
TStructuralSharing extends boolean = boolean,
|
|
437
|
+
>(
|
|
438
|
+
opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &
|
|
439
|
+
StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
|
|
440
|
+
): UseMatchesResult<TRouter, TSelected> {
|
|
412
441
|
const contextMatchId = React.useContext(matchContext)
|
|
413
442
|
|
|
414
443
|
return useMatches({
|
|
415
|
-
select: (matches) => {
|
|
444
|
+
select: (matches: Array<MakeRouteMatchUnion<TRouter>>) => {
|
|
416
445
|
matches = matches.slice(
|
|
417
446
|
matches.findIndex((d) => d.id === contextMatchId) + 1,
|
|
418
447
|
)
|
|
419
|
-
return opts?.select
|
|
420
|
-
? opts.select(matches as Array<TRouteMatch>)
|
|
421
|
-
: (matches as T)
|
|
448
|
+
return opts?.select ? opts.select(matches) : matches
|
|
422
449
|
},
|
|
423
|
-
|
|
450
|
+
structuralSharing: opts?.structuralSharing,
|
|
451
|
+
} as any)
|
|
424
452
|
}
|
package/src/RouterProvider.tsx
CHANGED
|
@@ -105,18 +105,21 @@ export type RouterProps<
|
|
|
105
105
|
RouterOptions<
|
|
106
106
|
TRouter['routeTree'],
|
|
107
107
|
NonNullable<TRouter['options']['trailingSlash']>,
|
|
108
|
+
NonNullable<TRouter['options']['defaultStructuralSharing']>,
|
|
108
109
|
TDehydrated
|
|
109
110
|
>,
|
|
110
111
|
'context'
|
|
111
112
|
> & {
|
|
112
113
|
router: Router<
|
|
113
114
|
TRouter['routeTree'],
|
|
114
|
-
NonNullable<TRouter['options']['trailingSlash']
|
|
115
|
+
NonNullable<TRouter['options']['trailingSlash']>,
|
|
116
|
+
NonNullable<TRouter['options']['defaultStructuralSharing']>
|
|
115
117
|
>
|
|
116
118
|
context?: Partial<
|
|
117
119
|
RouterOptions<
|
|
118
120
|
TRouter['routeTree'],
|
|
119
121
|
NonNullable<TRouter['options']['trailingSlash']>,
|
|
122
|
+
NonNullable<TRouter['options']['defaultStructuralSharing']>,
|
|
120
123
|
TDehydrated
|
|
121
124
|
>['context']
|
|
122
125
|
>
|
package/src/Transitioner.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import { useLayoutEffect, usePrevious } from './utils'
|
|
3
3
|
import { useRouter } from './useRouter'
|
|
4
4
|
import { useRouterState } from './useRouterState'
|
|
5
5
|
import { trimPathRight } from './path'
|
|
@@ -7,24 +7,23 @@ import { trimPathRight } from './path'
|
|
|
7
7
|
export function Transitioner() {
|
|
8
8
|
const router = useRouter()
|
|
9
9
|
const mountLoadForRouter = React.useRef({ router, mounted: false })
|
|
10
|
-
const
|
|
11
|
-
select: (
|
|
12
|
-
pick(s, ['isLoading', 'location', 'resolvedLocation', 'isTransitioning']),
|
|
10
|
+
const isLoading = useRouterState({
|
|
11
|
+
select: ({ isLoading }) => isLoading,
|
|
13
12
|
})
|
|
14
13
|
|
|
15
14
|
const [isTransitioning, startReactTransition_] = React.useTransition()
|
|
16
15
|
// Track pending state changes
|
|
17
16
|
const hasPendingMatches = useRouterState({
|
|
18
17
|
select: (s) => s.matches.some((d) => d.status === 'pending'),
|
|
18
|
+
structuralSharing: true,
|
|
19
19
|
})
|
|
20
20
|
|
|
21
|
-
const previousIsLoading = usePrevious(
|
|
21
|
+
const previousIsLoading = usePrevious(isLoading)
|
|
22
22
|
|
|
23
|
-
const isAnyPending =
|
|
24
|
-
routerState.isLoading || isTransitioning || hasPendingMatches
|
|
23
|
+
const isAnyPending = isLoading || isTransitioning || hasPendingMatches
|
|
25
24
|
const previousIsAnyPending = usePrevious(isAnyPending)
|
|
26
25
|
|
|
27
|
-
const isPagePending =
|
|
26
|
+
const isPagePending = isLoading || hasPendingMatches
|
|
28
27
|
const previousIsPagePending = usePrevious(isPagePending)
|
|
29
28
|
|
|
30
29
|
if (!router.isServer) {
|
|
@@ -81,7 +80,7 @@ export function Transitioner() {
|
|
|
81
80
|
|
|
82
81
|
useLayoutEffect(() => {
|
|
83
82
|
// The router was loading and now it's not
|
|
84
|
-
if (previousIsLoading && !
|
|
83
|
+
if (previousIsLoading && !isLoading) {
|
|
85
84
|
const toLocation = router.state.location
|
|
86
85
|
const fromLocation = router.state.resolvedLocation
|
|
87
86
|
const pathChanged = fromLocation.pathname !== toLocation.pathname
|
|
@@ -93,7 +92,7 @@ export function Transitioner() {
|
|
|
93
92
|
pathChanged,
|
|
94
93
|
})
|
|
95
94
|
}
|
|
96
|
-
}, [previousIsLoading, router,
|
|
95
|
+
}, [previousIsLoading, router, isLoading])
|
|
97
96
|
|
|
98
97
|
useLayoutEffect(() => {
|
|
99
98
|
// emit onBeforeRouteMount
|
package/src/fileRoute.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import warning from 'tiny-warning'
|
|
2
2
|
import { createRoute } from './route'
|
|
3
|
+
|
|
3
4
|
import { useMatch } from './useMatch'
|
|
4
5
|
import { useLoaderDeps } from './useLoaderDeps'
|
|
5
6
|
import { useLoaderData } from './useLoaderData'
|
|
6
7
|
import { useSearch } from './useSearch'
|
|
7
8
|
import { useParams } from './useParams'
|
|
8
9
|
import { useNavigate } from './useNavigate'
|
|
10
|
+
import type { UseParamsRoute } from './useParams'
|
|
11
|
+
import type { UseMatchRoute } from './useMatch'
|
|
12
|
+
import type { UseSearchRoute } from './useSearch'
|
|
9
13
|
import type { Constrain } from './utils'
|
|
10
14
|
import type {
|
|
11
15
|
AnyContext,
|
|
@@ -20,9 +24,11 @@ import type {
|
|
|
20
24
|
RouteLoaderFn,
|
|
21
25
|
UpdatableRouteOptions,
|
|
22
26
|
} from './route'
|
|
23
|
-
import type { MakeRouteMatch } from './Matches'
|
|
24
27
|
import type { RegisteredRouter } from './router'
|
|
25
28
|
import type { RouteById, RouteIds } from './routeInfo'
|
|
29
|
+
import type { UseLoaderDepsRoute } from './useLoaderDeps'
|
|
30
|
+
import type { UseLoaderDataRoute } from './useLoaderData'
|
|
31
|
+
import type { UseRouteContextRoute } from './useRouteContext'
|
|
26
32
|
|
|
27
33
|
export interface FileRoutesByPath {
|
|
28
34
|
// '/': {
|
|
@@ -208,48 +214,42 @@ export class LazyRoute<TRoute extends AnyRoute> {
|
|
|
208
214
|
;(this as any).$$typeof = Symbol.for('react.memo')
|
|
209
215
|
}
|
|
210
216
|
|
|
211
|
-
useMatch =
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
>(opts?: {
|
|
218
|
-
select?: (match: TRouteMatch) => TSelected
|
|
219
|
-
}): TSelected => {
|
|
220
|
-
return useMatch({ select: opts?.select, from: this.options.id })
|
|
217
|
+
useMatch: UseMatchRoute<TRoute['id']> = (opts) => {
|
|
218
|
+
return useMatch({
|
|
219
|
+
select: opts?.select,
|
|
220
|
+
from: this.options.id,
|
|
221
|
+
structuralSharing: opts?.structuralSharing,
|
|
222
|
+
} as any) as any
|
|
221
223
|
}
|
|
222
224
|
|
|
223
|
-
useRouteContext
|
|
224
|
-
select?: (s: TRoute['types']['allContext']) => TSelected
|
|
225
|
-
}): TSelected => {
|
|
225
|
+
useRouteContext: UseRouteContextRoute<TRoute['id']> = (opts) => {
|
|
226
226
|
return useMatch({
|
|
227
227
|
from: this.options.id,
|
|
228
228
|
select: (d: any) => (opts?.select ? opts.select(d.context) : d.context),
|
|
229
|
-
})
|
|
229
|
+
}) as any
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
useSearch
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
232
|
+
useSearch: UseSearchRoute<TRoute['id']> = (opts) => {
|
|
233
|
+
return useSearch({
|
|
234
|
+
select: opts?.select,
|
|
235
|
+
structuralSharing: opts?.structuralSharing,
|
|
236
|
+
from: this.options.id,
|
|
237
|
+
} as any)
|
|
236
238
|
}
|
|
237
239
|
|
|
238
|
-
useParams
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
useParams: UseParamsRoute<TRoute['id']> = (opts) => {
|
|
241
|
+
return useParams({
|
|
242
|
+
select: opts?.select,
|
|
243
|
+
structuralSharing: opts?.structuralSharing,
|
|
244
|
+
from: this.options.id,
|
|
245
|
+
} as any)
|
|
242
246
|
}
|
|
243
247
|
|
|
244
|
-
useLoaderDeps
|
|
245
|
-
select?: (s: TRoute['types']['loaderDeps']) => TSelected
|
|
246
|
-
}): TSelected => {
|
|
248
|
+
useLoaderDeps: UseLoaderDepsRoute<TRoute['id']> = (opts) => {
|
|
247
249
|
return useLoaderDeps({ ...opts, from: this.options.id } as any)
|
|
248
250
|
}
|
|
249
251
|
|
|
250
|
-
useLoaderData
|
|
251
|
-
select?: (s: TRoute['types']['loaderData']) => TSelected
|
|
252
|
-
}): TSelected => {
|
|
252
|
+
useLoaderData: UseLoaderDataRoute<TRoute['id']> = (opts) => {
|
|
253
253
|
return useLoaderData({ ...opts, from: this.options.id } as any)
|
|
254
254
|
}
|
|
255
255
|
|
package/src/link.tsx
CHANGED
|
@@ -233,10 +233,10 @@ export interface MaskOptions<
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
export type ToMaskOptions<
|
|
236
|
-
|
|
236
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
237
237
|
TMaskFrom extends string = string,
|
|
238
238
|
TMaskTo extends string = '.',
|
|
239
|
-
> = ToSubOptions<
|
|
239
|
+
> = ToSubOptions<TRouter, TMaskFrom, TMaskTo> & {
|
|
240
240
|
unmaskOnReload?: boolean
|
|
241
241
|
}
|
|
242
242
|
|
|
@@ -645,7 +645,10 @@ export function useLinkProps<
|
|
|
645
645
|
}, [to])
|
|
646
646
|
|
|
647
647
|
// subscribe to search params to re-build location if it changes
|
|
648
|
-
const currentSearch = useRouterState({
|
|
648
|
+
const currentSearch = useRouterState({
|
|
649
|
+
select: (s) => s.location.search,
|
|
650
|
+
structuralSharing: true as any,
|
|
651
|
+
})
|
|
649
652
|
|
|
650
653
|
const next = React.useMemo(
|
|
651
654
|
() => router.buildLocation(options as any),
|
package/src/path.ts
CHANGED
|
@@ -204,6 +204,8 @@ interface InterpolatePathOptions {
|
|
|
204
204
|
params: Record<string, unknown>
|
|
205
205
|
leaveWildcards?: boolean
|
|
206
206
|
leaveParams?: boolean
|
|
207
|
+
// Map of encoded chars to decoded chars (e.g. '%40' -> '@') that should remain decoded in path params
|
|
208
|
+
decodeCharMap?: Map<string, string>
|
|
207
209
|
}
|
|
208
210
|
|
|
209
211
|
export function interpolatePath({
|
|
@@ -211,6 +213,7 @@ export function interpolatePath({
|
|
|
211
213
|
params,
|
|
212
214
|
leaveWildcards,
|
|
213
215
|
leaveParams,
|
|
216
|
+
decodeCharMap,
|
|
214
217
|
}: InterpolatePathOptions) {
|
|
215
218
|
const interpolatedPathSegments = parsePathname(path)
|
|
216
219
|
const encodedParams: any = {}
|
|
@@ -222,7 +225,9 @@ export function interpolatePath({
|
|
|
222
225
|
// the splat/catch-all routes shouldn't have the '/' encoded out
|
|
223
226
|
encodedParams[key] = isValueString ? encodeURI(value) : value
|
|
224
227
|
} else {
|
|
225
|
-
encodedParams[key] = isValueString
|
|
228
|
+
encodedParams[key] = isValueString
|
|
229
|
+
? encodePathParam(value, decodeCharMap)
|
|
230
|
+
: value
|
|
226
231
|
}
|
|
227
232
|
}
|
|
228
233
|
|
|
@@ -247,6 +252,16 @@ export function interpolatePath({
|
|
|
247
252
|
)
|
|
248
253
|
}
|
|
249
254
|
|
|
255
|
+
function encodePathParam(value: string, decodeCharMap?: Map<string, string>) {
|
|
256
|
+
let encoded = encodeURIComponent(value)
|
|
257
|
+
if (decodeCharMap) {
|
|
258
|
+
for (const [encodedChar, char] of decodeCharMap) {
|
|
259
|
+
encoded = encoded.replaceAll(encodedChar, char)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return encoded
|
|
263
|
+
}
|
|
264
|
+
|
|
250
265
|
export function matchPathname(
|
|
251
266
|
basepath: string,
|
|
252
267
|
currentPathname: string,
|
package/src/route.ts
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import invariant from 'tiny-invariant'
|
|
2
|
-
import { useMatch } from './useMatch'
|
|
3
|
-
import { useLoaderDeps } from './useLoaderDeps'
|
|
4
|
-
import { useLoaderData } from './useLoaderData'
|
|
5
2
|
import { joinPaths, trimPathLeft } from './path'
|
|
3
|
+
import { useLoaderData } from './useLoaderData'
|
|
4
|
+
import { useLoaderDeps } from './useLoaderDeps'
|
|
6
5
|
import { useParams } from './useParams'
|
|
7
6
|
import { useSearch } from './useSearch'
|
|
8
7
|
import { notFound } from './not-found'
|
|
9
8
|
import { useNavigate } from './useNavigate'
|
|
10
9
|
import { rootRouteId } from './root'
|
|
10
|
+
import { useMatch } from './useMatch'
|
|
11
|
+
import type { UseLoaderDataRoute } from './useLoaderData'
|
|
12
|
+
import type { UseMatchRoute } from './useMatch'
|
|
13
|
+
import type { UseLoaderDepsRoute } from './useLoaderDeps'
|
|
14
|
+
import type { UseParamsRoute } from './useParams'
|
|
15
|
+
import type { UseSearchRoute } from './useSearch'
|
|
11
16
|
import type * as React from 'react'
|
|
12
17
|
import type { RootRouteId } from './root'
|
|
13
18
|
import type { UseNavigateResult } from './useNavigate'
|
|
14
19
|
import type {
|
|
15
|
-
MakeRouteMatch,
|
|
16
20
|
MakeRouteMatchFromRoute,
|
|
17
21
|
MakeRouteMatchUnion,
|
|
18
22
|
RouteMatch,
|
|
@@ -25,6 +29,7 @@ import type { Assign, Constrain, Expand, NoInfer } from './utils'
|
|
|
25
29
|
import type { BuildLocationFn, NavigateFn } from './RouterProvider'
|
|
26
30
|
import type { NotFoundError } from './not-found'
|
|
27
31
|
import type { LazyRoute } from './fileRoute'
|
|
32
|
+
import type { UseRouteContextRoute } from './useRouteContext'
|
|
28
33
|
|
|
29
34
|
export type AnyPathParams = {}
|
|
30
35
|
|
|
@@ -845,60 +850,42 @@ export class RouteApi<TId, TRouter extends AnyRouter = RegisteredRouter> {
|
|
|
845
850
|
this.id = id as any
|
|
846
851
|
}
|
|
847
852
|
|
|
848
|
-
useMatch =
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
}): TSelected => {
|
|
855
|
-
return useMatch({ select: opts?.select, from: this.id })
|
|
853
|
+
useMatch: UseMatchRoute<TId> = (opts) => {
|
|
854
|
+
return useMatch({
|
|
855
|
+
select: opts?.select,
|
|
856
|
+
from: this.id,
|
|
857
|
+
structuralSharing: opts?.structuralSharing,
|
|
858
|
+
} as any) as any
|
|
856
859
|
}
|
|
857
860
|
|
|
858
|
-
useRouteContext =
|
|
859
|
-
TSelected = Expand<RouteTypesById<TRouter, TId>['allContext']>,
|
|
860
|
-
>(opts?: {
|
|
861
|
-
select?: (
|
|
862
|
-
s: Expand<RouteTypesById<TRouter, TId>['allContext']>,
|
|
863
|
-
) => TSelected
|
|
864
|
-
}): TSelected => {
|
|
861
|
+
useRouteContext: UseRouteContextRoute<TId> = (opts) => {
|
|
865
862
|
return useMatch({
|
|
866
|
-
from: this.id,
|
|
867
|
-
select: (d
|
|
868
|
-
})
|
|
863
|
+
from: this.id as any,
|
|
864
|
+
select: (d) => (opts?.select ? opts.select(d.context) : d.context),
|
|
865
|
+
}) as any
|
|
869
866
|
}
|
|
870
867
|
|
|
871
|
-
useSearch =
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
}): TSelected => {
|
|
878
|
-
return useSearch({ ...opts, from: this.id })
|
|
868
|
+
useSearch: UseSearchRoute<TId> = (opts) => {
|
|
869
|
+
return useSearch({
|
|
870
|
+
select: opts?.select,
|
|
871
|
+
structuralSharing: opts?.structuralSharing,
|
|
872
|
+
from: this.id,
|
|
873
|
+
} as any)
|
|
879
874
|
}
|
|
880
875
|
|
|
881
|
-
useParams =
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
876
|
+
useParams: UseParamsRoute<TId> = (opts) => {
|
|
877
|
+
return useParams({
|
|
878
|
+
select: opts?.select,
|
|
879
|
+
structuralSharing: opts?.structuralSharing,
|
|
880
|
+
from: this.id,
|
|
881
|
+
} as any)
|
|
887
882
|
}
|
|
888
883
|
|
|
889
|
-
useLoaderDeps =
|
|
890
|
-
TSelected = RouteTypesById<TRouter, TId>['loaderDeps'],
|
|
891
|
-
>(opts?: {
|
|
892
|
-
select?: (s: RouteTypesById<TRouter, TId>['loaderDeps']) => TSelected
|
|
893
|
-
}): TSelected => {
|
|
884
|
+
useLoaderDeps: UseLoaderDepsRoute<TId> = (opts) => {
|
|
894
885
|
return useLoaderDeps({ ...opts, from: this.id, strict: false } as any)
|
|
895
886
|
}
|
|
896
887
|
|
|
897
|
-
useLoaderData =
|
|
898
|
-
TSelected = RouteTypesById<TRouter, TId>['loaderData'],
|
|
899
|
-
>(opts?: {
|
|
900
|
-
select?: (s: RouteTypesById<TRouter, TId>['loaderData']) => TSelected
|
|
901
|
-
}): TSelected => {
|
|
888
|
+
useLoaderData: UseLoaderDataRoute<TId> = (opts) => {
|
|
902
889
|
return useLoaderData({ ...opts, from: this.id, strict: false } as any)
|
|
903
890
|
}
|
|
904
891
|
|
|
@@ -1258,74 +1245,43 @@ export class Route<
|
|
|
1258
1245
|
return this
|
|
1259
1246
|
}
|
|
1260
1247
|
|
|
1261
|
-
useMatch =
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
select?: (match: TRouteMatch) => TSelected
|
|
1268
|
-
}): TSelected => {
|
|
1269
|
-
return useMatch({ ...opts, from: this.id })
|
|
1248
|
+
useMatch: UseMatchRoute<TId> = (opts) => {
|
|
1249
|
+
return useMatch({
|
|
1250
|
+
select: opts?.select,
|
|
1251
|
+
from: this.id,
|
|
1252
|
+
structuralSharing: opts?.structuralSharing,
|
|
1253
|
+
} as any) as any
|
|
1270
1254
|
}
|
|
1271
1255
|
|
|
1272
|
-
useRouteContext =
|
|
1273
|
-
TSelected = Expand<
|
|
1274
|
-
ResolveAllContext<
|
|
1275
|
-
TParentRoute,
|
|
1276
|
-
TRouterContext,
|
|
1277
|
-
TRouteContextFn,
|
|
1278
|
-
TBeforeLoadFn
|
|
1279
|
-
>
|
|
1280
|
-
>,
|
|
1281
|
-
>(opts?: {
|
|
1282
|
-
select?: (
|
|
1283
|
-
search: Expand<
|
|
1284
|
-
ResolveAllContext<
|
|
1285
|
-
TParentRoute,
|
|
1286
|
-
TRouterContext,
|
|
1287
|
-
TRouteContextFn,
|
|
1288
|
-
TBeforeLoadFn
|
|
1289
|
-
>
|
|
1290
|
-
>,
|
|
1291
|
-
) => TSelected
|
|
1292
|
-
}): TSelected => {
|
|
1256
|
+
useRouteContext: UseRouteContextRoute<TId> = (opts?) => {
|
|
1293
1257
|
return useMatch({
|
|
1294
1258
|
...opts,
|
|
1295
1259
|
from: this.id,
|
|
1296
|
-
select: (d
|
|
1297
|
-
})
|
|
1260
|
+
select: (d) => (opts?.select ? opts.select(d.context) : d.context),
|
|
1261
|
+
}) as any
|
|
1298
1262
|
}
|
|
1299
1263
|
|
|
1300
|
-
useSearch =
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
}): TSelected => {
|
|
1307
|
-
return useSearch({ ...opts, from: this.id })
|
|
1264
|
+
useSearch: UseSearchRoute<TId> = (opts) => {
|
|
1265
|
+
return useSearch({
|
|
1266
|
+
select: opts?.select,
|
|
1267
|
+
structuralSharing: opts?.structuralSharing,
|
|
1268
|
+
from: this.id,
|
|
1269
|
+
} as any)
|
|
1308
1270
|
}
|
|
1309
1271
|
|
|
1310
|
-
useParams =
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
}): TSelected => {
|
|
1317
|
-
return useParams({ ...opts, from: this.id })
|
|
1272
|
+
useParams: UseParamsRoute<TId> = (opts) => {
|
|
1273
|
+
return useParams({
|
|
1274
|
+
select: opts?.select,
|
|
1275
|
+
structuralSharing: opts?.structuralSharing,
|
|
1276
|
+
from: this.id,
|
|
1277
|
+
} as any)
|
|
1318
1278
|
}
|
|
1319
1279
|
|
|
1320
|
-
useLoaderDeps
|
|
1321
|
-
select?: (s: TLoaderDeps) => TSelected
|
|
1322
|
-
}): TSelected => {
|
|
1280
|
+
useLoaderDeps: UseLoaderDepsRoute<TId> = (opts) => {
|
|
1323
1281
|
return useLoaderDeps({ ...opts, from: this.id } as any)
|
|
1324
1282
|
}
|
|
1325
1283
|
|
|
1326
|
-
useLoaderData
|
|
1327
|
-
select?: (search: ResolveLoaderData<TLoaderFn>) => TSelected
|
|
1328
|
-
}): TSelected => {
|
|
1284
|
+
useLoaderData: UseLoaderDataRoute<TId> = (opts) => {
|
|
1329
1285
|
return useLoaderData({ ...opts, from: this.id } as any)
|
|
1330
1286
|
}
|
|
1331
1287
|
|
|
@@ -1643,7 +1599,7 @@ export function createRouteMask<
|
|
|
1643
1599
|
>(
|
|
1644
1600
|
opts: {
|
|
1645
1601
|
routeTree: TRouteTree
|
|
1646
|
-
} & ToMaskOptions<Router<TRouteTree, 'never'>, TFrom, TTo>,
|
|
1602
|
+
} & ToMaskOptions<Router<TRouteTree, 'never', boolean>, TFrom, TTo>,
|
|
1647
1603
|
): RouteMask<TRouteTree> {
|
|
1648
1604
|
return opts as any
|
|
1649
1605
|
}
|