@depup/tanstack__react-router 1.167.4-depup.0 → 1.168.1-depup.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/README.md +3 -4
- package/changes.json +2 -6
- 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 +11 -13
- 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/src/link.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
|
+
import { useStore } from '@tanstack/react-store'
|
|
2
3
|
import { flushSync } from 'react-dom'
|
|
3
4
|
import {
|
|
4
5
|
deepEqual,
|
|
@@ -9,7 +10,6 @@ import {
|
|
|
9
10
|
removeTrailingSlash,
|
|
10
11
|
} from '@tanstack/router-core'
|
|
11
12
|
import { isServer } from '@tanstack/router-core/isServer'
|
|
12
|
-
import { useRouterState } from './useRouterState'
|
|
13
13
|
import { useRouter } from './useRouter'
|
|
14
14
|
|
|
15
15
|
import { useForwardedRef, useIntersectionObserver } from './utils'
|
|
@@ -102,7 +102,7 @@ export function useLinkProps<
|
|
|
102
102
|
//
|
|
103
103
|
// For SSR parity (to avoid hydration errors), we still compute the link's
|
|
104
104
|
// active status on the server, but we avoid creating any router-state
|
|
105
|
-
// subscriptions by reading from
|
|
105
|
+
// subscriptions by reading from the location store directly.
|
|
106
106
|
//
|
|
107
107
|
// Note: `location.hash` is not available on the server.
|
|
108
108
|
// ==========================================================================
|
|
@@ -204,7 +204,7 @@ export function useLinkProps<
|
|
|
204
204
|
const isActive = (() => {
|
|
205
205
|
if (externalLink) return false
|
|
206
206
|
|
|
207
|
-
const currentLocation = router.
|
|
207
|
+
const currentLocation = router.stores.location.state
|
|
208
208
|
|
|
209
209
|
const exact = activeOptions?.exact ?? false
|
|
210
210
|
|
|
@@ -377,32 +377,13 @@ export function useLinkProps<
|
|
|
377
377
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
378
378
|
const isHydrated = useHydrated()
|
|
379
379
|
|
|
380
|
-
// subscribe to path/search/hash/params to re-build location when they change
|
|
381
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
382
|
-
const currentLocationState = useRouterState({
|
|
383
|
-
select: (s) => {
|
|
384
|
-
const leaf = s.matches[s.matches.length - 1]
|
|
385
|
-
return {
|
|
386
|
-
search: leaf?.search,
|
|
387
|
-
hash: s.location.hash,
|
|
388
|
-
path: leaf?.pathname, // path + params
|
|
389
|
-
}
|
|
390
|
-
},
|
|
391
|
-
structuralSharing: true as any,
|
|
392
|
-
})
|
|
393
|
-
|
|
394
|
-
const from = options.from
|
|
395
|
-
|
|
396
380
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
397
381
|
const _options = React.useMemo(
|
|
398
|
-
() =>
|
|
399
|
-
return { ...options, from }
|
|
400
|
-
},
|
|
382
|
+
() => options,
|
|
401
383
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
402
384
|
[
|
|
403
385
|
router,
|
|
404
|
-
|
|
405
|
-
from,
|
|
386
|
+
options.from,
|
|
406
387
|
options._fromLocation,
|
|
407
388
|
options.hash,
|
|
408
389
|
options.to,
|
|
@@ -415,11 +396,18 @@ export function useLinkProps<
|
|
|
415
396
|
)
|
|
416
397
|
|
|
417
398
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
399
|
+
const currentLocation = useStore(
|
|
400
|
+
router.stores.location,
|
|
401
|
+
(l) => l,
|
|
402
|
+
(prev, next) => prev.href === next.href,
|
|
421
403
|
)
|
|
422
404
|
|
|
405
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
406
|
+
const next = React.useMemo(() => {
|
|
407
|
+
const opts = { _fromLocation: currentLocation, ..._options }
|
|
408
|
+
return router.buildLocation(opts as any)
|
|
409
|
+
}, [router, currentLocation, _options])
|
|
410
|
+
|
|
423
411
|
// Use publicHref - it contains the correct href for display
|
|
424
412
|
// When a rewrite changes the origin, publicHref is the full URL
|
|
425
413
|
// Otherwise it's the origin-stripped path
|
|
@@ -474,54 +462,61 @@ export function useLinkProps<
|
|
|
474
462
|
}, [to, hrefOption, router.protocolAllowlist])
|
|
475
463
|
|
|
476
464
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
477
|
-
const isActive =
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
return false
|
|
488
|
-
}
|
|
489
|
-
} else {
|
|
490
|
-
const currentPathSplit = removeTrailingSlash(
|
|
491
|
-
s.location.pathname,
|
|
492
|
-
router.basepath,
|
|
493
|
-
)
|
|
494
|
-
const nextPathSplit = removeTrailingSlash(
|
|
495
|
-
next.pathname,
|
|
496
|
-
router.basepath,
|
|
497
|
-
)
|
|
498
|
-
|
|
499
|
-
const pathIsFuzzyEqual =
|
|
500
|
-
currentPathSplit.startsWith(nextPathSplit) &&
|
|
501
|
-
(currentPathSplit.length === nextPathSplit.length ||
|
|
502
|
-
currentPathSplit[nextPathSplit.length] === '/')
|
|
503
|
-
|
|
504
|
-
if (!pathIsFuzzyEqual) {
|
|
505
|
-
return false
|
|
506
|
-
}
|
|
465
|
+
const isActive = React.useMemo(() => {
|
|
466
|
+
if (externalLink) return false
|
|
467
|
+
if (activeOptions?.exact) {
|
|
468
|
+
const testExact = exactPathTest(
|
|
469
|
+
currentLocation.pathname,
|
|
470
|
+
next.pathname,
|
|
471
|
+
router.basepath,
|
|
472
|
+
)
|
|
473
|
+
if (!testExact) {
|
|
474
|
+
return false
|
|
507
475
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
476
|
+
} else {
|
|
477
|
+
const currentPathSplit = removeTrailingSlash(
|
|
478
|
+
currentLocation.pathname,
|
|
479
|
+
router.basepath,
|
|
480
|
+
)
|
|
481
|
+
const nextPathSplit = removeTrailingSlash(next.pathname, router.basepath)
|
|
482
|
+
|
|
483
|
+
const pathIsFuzzyEqual =
|
|
484
|
+
currentPathSplit.startsWith(nextPathSplit) &&
|
|
485
|
+
(currentPathSplit.length === nextPathSplit.length ||
|
|
486
|
+
currentPathSplit[nextPathSplit.length] === '/')
|
|
487
|
+
|
|
488
|
+
if (!pathIsFuzzyEqual) {
|
|
489
|
+
return false
|
|
517
490
|
}
|
|
491
|
+
}
|
|
518
492
|
|
|
519
|
-
|
|
520
|
-
|
|
493
|
+
if (activeOptions?.includeSearch ?? true) {
|
|
494
|
+
const searchTest = deepEqual(currentLocation.search, next.search, {
|
|
495
|
+
partial: !activeOptions?.exact,
|
|
496
|
+
ignoreUndefined: !activeOptions?.explicitUndefined,
|
|
497
|
+
})
|
|
498
|
+
if (!searchTest) {
|
|
499
|
+
return false
|
|
521
500
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (activeOptions?.includeHash) {
|
|
504
|
+
return isHydrated && currentLocation.hash === next.hash
|
|
505
|
+
}
|
|
506
|
+
return true
|
|
507
|
+
}, [
|
|
508
|
+
activeOptions?.exact,
|
|
509
|
+
activeOptions?.explicitUndefined,
|
|
510
|
+
activeOptions?.includeHash,
|
|
511
|
+
activeOptions?.includeSearch,
|
|
512
|
+
currentLocation,
|
|
513
|
+
externalLink,
|
|
514
|
+
isHydrated,
|
|
515
|
+
next.hash,
|
|
516
|
+
next.pathname,
|
|
517
|
+
next.search,
|
|
518
|
+
router.basepath,
|
|
519
|
+
])
|
|
525
520
|
|
|
526
521
|
// Get the active props
|
|
527
522
|
const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> = isActive
|
package/src/not-found.tsx
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import { isNotFound } from '@tanstack/router-core'
|
|
3
|
+
import { isServer } from '@tanstack/router-core/isServer'
|
|
4
|
+
import { useStore } from '@tanstack/react-store'
|
|
3
5
|
import { CatchBoundary } from './CatchBoundary'
|
|
4
|
-
import {
|
|
6
|
+
import { useRouter } from './useRouter'
|
|
5
7
|
import type { ErrorInfo } from 'react'
|
|
6
8
|
import type { NotFoundError } from '@tanstack/router-core'
|
|
7
9
|
|
|
@@ -10,10 +12,45 @@ export function CatchNotFound(props: {
|
|
|
10
12
|
onCatch?: (error: Error, errorInfo: ErrorInfo) => void
|
|
11
13
|
children: React.ReactNode
|
|
12
14
|
}) {
|
|
15
|
+
const router = useRouter()
|
|
16
|
+
|
|
17
|
+
if (isServer ?? router.isServer) {
|
|
18
|
+
const pathname = router.stores.location.state.pathname
|
|
19
|
+
const status = router.stores.status.state
|
|
20
|
+
const resetKey = `not-found-${pathname}-${status}`
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<CatchBoundary
|
|
24
|
+
getResetKey={() => resetKey}
|
|
25
|
+
onCatch={(error, errorInfo) => {
|
|
26
|
+
if (isNotFound(error)) {
|
|
27
|
+
props.onCatch?.(error, errorInfo)
|
|
28
|
+
} else {
|
|
29
|
+
throw error
|
|
30
|
+
}
|
|
31
|
+
}}
|
|
32
|
+
errorComponent={({ error }) => {
|
|
33
|
+
if (isNotFound(error)) {
|
|
34
|
+
return props.fallback?.(error)
|
|
35
|
+
} else {
|
|
36
|
+
throw error
|
|
37
|
+
}
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
{props.children}
|
|
41
|
+
</CatchBoundary>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
13
45
|
// TODO: Some way for the user to programmatically reset the not-found boundary?
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
46
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
|
|
47
|
+
const pathname = useStore(
|
|
48
|
+
router.stores.location,
|
|
49
|
+
(location) => location.pathname,
|
|
50
|
+
)
|
|
51
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
|
|
52
|
+
const status = useStore(router.stores.status, (status) => status)
|
|
53
|
+
const resetKey = `not-found-${pathname}-${status}`
|
|
17
54
|
|
|
18
55
|
return (
|
|
19
56
|
<CatchBoundary
|
package/src/router.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RouterCore } from '@tanstack/router-core'
|
|
2
2
|
import { createFileRoute, createLazyFileRoute } from './fileRoute'
|
|
3
|
+
import { getStoreFactory } from './routerStores'
|
|
3
4
|
import type { RouterHistory } from '@tanstack/history'
|
|
4
5
|
import type {
|
|
5
6
|
AnyRoute,
|
|
@@ -114,7 +115,7 @@ export class Router<
|
|
|
114
115
|
TDehydrated
|
|
115
116
|
>,
|
|
116
117
|
) {
|
|
117
|
-
super(options)
|
|
118
|
+
super(options, getStoreFactory)
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { batch, createStore } from '@tanstack/react-store'
|
|
2
|
+
import {
|
|
3
|
+
createNonReactiveMutableStore,
|
|
4
|
+
createNonReactiveReadonlyStore,
|
|
5
|
+
} from '@tanstack/router-core'
|
|
6
|
+
import { isServer } from '@tanstack/router-core/isServer'
|
|
7
|
+
import type { Readable } from '@tanstack/react-store'
|
|
8
|
+
import type { GetStoreConfig } from '@tanstack/router-core'
|
|
9
|
+
|
|
10
|
+
declare module '@tanstack/router-core' {
|
|
11
|
+
export interface RouterReadableStore<TValue> extends Readable<TValue> {}
|
|
12
|
+
}
|
|
13
|
+
export const getStoreFactory: GetStoreConfig = (opts) => {
|
|
14
|
+
if (isServer ?? opts.isServer) {
|
|
15
|
+
return {
|
|
16
|
+
createMutableStore: createNonReactiveMutableStore,
|
|
17
|
+
createReadonlyStore: createNonReactiveReadonlyStore,
|
|
18
|
+
batch: (fn) => fn(),
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
createMutableStore: createStore,
|
|
23
|
+
createReadonlyStore: createStore,
|
|
24
|
+
batch: batch,
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/ssr/RouterClient.tsx
CHANGED
|
@@ -7,7 +7,7 @@ let hydrationPromise: Promise<void | Array<Array<void>>> | undefined
|
|
|
7
7
|
|
|
8
8
|
export function RouterClient(props: { router: AnyRouter }) {
|
|
9
9
|
if (!hydrationPromise) {
|
|
10
|
-
if (!props.router.state.
|
|
10
|
+
if (!props.router.stores.matchesId.state.length) {
|
|
11
11
|
hydrationPromise = hydrate(props.router)
|
|
12
12
|
} else {
|
|
13
13
|
hydrationPromise = Promise.resolve()
|
|
@@ -36,7 +36,7 @@ export const renderRouterToStream = async ({
|
|
|
36
36
|
stream as unknown as ReadableStream,
|
|
37
37
|
)
|
|
38
38
|
return new Response(responseStream as any, {
|
|
39
|
-
status: router.
|
|
39
|
+
status: router.stores.statusCode.state,
|
|
40
40
|
headers: responseHeaders,
|
|
41
41
|
})
|
|
42
42
|
}
|
|
@@ -79,7 +79,7 @@ export const renderRouterToStream = async ({
|
|
|
79
79
|
reactAppPassthrough,
|
|
80
80
|
)
|
|
81
81
|
return new Response(responseStream as any, {
|
|
82
|
-
status: router.
|
|
82
|
+
status: router.stores.statusCode.state,
|
|
83
83
|
headers: responseHeaders,
|
|
84
84
|
})
|
|
85
85
|
}
|
package/src/useCanGoBack.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useStore } from '@tanstack/react-store'
|
|
2
|
+
import { isServer } from '@tanstack/router-core/isServer'
|
|
3
|
+
import { useRouter } from './useRouter'
|
|
2
4
|
|
|
3
5
|
export function useCanGoBack() {
|
|
4
|
-
|
|
6
|
+
const router = useRouter()
|
|
7
|
+
|
|
8
|
+
if (isServer ?? router.isServer) {
|
|
9
|
+
return router.stores.location.state.state.__TSR_index !== 0
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
|
|
13
|
+
return useStore(
|
|
14
|
+
router.stores.location,
|
|
15
|
+
(location) => location.state.__TSR_index !== 0,
|
|
16
|
+
)
|
|
5
17
|
}
|
package/src/useLocation.tsx
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useStore } from '@tanstack/react-store'
|
|
2
|
+
import { useRef } from 'react'
|
|
3
|
+
import { replaceEqualDeep } from '@tanstack/router-core'
|
|
4
|
+
import { isServer } from '@tanstack/router-core/isServer'
|
|
5
|
+
import { useRouter } from './useRouter'
|
|
2
6
|
import type {
|
|
3
7
|
StructuralSharingOption,
|
|
4
8
|
ValidateSelected,
|
|
@@ -45,8 +49,31 @@ export function useLocation<
|
|
|
45
49
|
opts?: UseLocationBaseOptions<TRouter, TSelected, TStructuralSharing> &
|
|
46
50
|
StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
|
|
47
51
|
): UseLocationResult<TRouter, TSelected> {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
const router = useRouter<TRouter>()
|
|
53
|
+
|
|
54
|
+
if (isServer ?? router.isServer) {
|
|
55
|
+
const location = router.stores.location.state
|
|
56
|
+
return (
|
|
57
|
+
opts?.select ? opts.select(location as any) : location
|
|
58
|
+
) as UseLocationResult<TRouter, TSelected>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const previousResult =
|
|
62
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
|
|
63
|
+
useRef<ValidateSelected<TRouter, TSelected, TStructuralSharing>>(undefined)
|
|
64
|
+
|
|
65
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
|
|
66
|
+
return useStore(router.stores.location, (location) => {
|
|
67
|
+
const selected = (
|
|
68
|
+
opts?.select ? opts.select(location as any) : location
|
|
69
|
+
) as ValidateSelected<TRouter, TSelected, TStructuralSharing>
|
|
70
|
+
|
|
71
|
+
if (opts?.structuralSharing ?? router.options.defaultStructuralSharing) {
|
|
72
|
+
const shared = replaceEqualDeep(previousResult.current, selected)
|
|
73
|
+
previousResult.current = shared
|
|
74
|
+
return shared
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return selected
|
|
78
|
+
}) as UseLocationResult<TRouter, TSelected>
|
|
52
79
|
}
|
package/src/useMatch.tsx
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
|
+
import { useStore } from '@tanstack/react-store'
|
|
3
|
+
import { replaceEqualDeep } from '@tanstack/router-core'
|
|
4
|
+
import { isServer } from '@tanstack/router-core/isServer'
|
|
2
5
|
import invariant from 'tiny-invariant'
|
|
3
|
-
import { useRouterState } from './useRouterState'
|
|
4
6
|
import { dummyMatchContext, matchContext } from './matchContext'
|
|
7
|
+
import { useRouter } from './useRouter'
|
|
5
8
|
import type {
|
|
6
9
|
StructuralSharingOption,
|
|
7
10
|
ValidateSelected,
|
|
@@ -16,6 +19,12 @@ import type {
|
|
|
16
19
|
ThrowOrOptional,
|
|
17
20
|
} from '@tanstack/router-core'
|
|
18
21
|
|
|
22
|
+
const dummyStore = {
|
|
23
|
+
state: undefined,
|
|
24
|
+
get: () => undefined,
|
|
25
|
+
subscribe: () => () => {},
|
|
26
|
+
} as any
|
|
27
|
+
|
|
19
28
|
export interface UseMatchBaseOptions<
|
|
20
29
|
TRouter extends AnyRouter,
|
|
21
30
|
TFrom,
|
|
@@ -96,28 +105,59 @@ export function useMatch<
|
|
|
96
105
|
TStructuralSharing
|
|
97
106
|
>,
|
|
98
107
|
): ThrowOrOptional<UseMatchResult<TRouter, TFrom, TStrict, TSelected>, TThrow> {
|
|
108
|
+
const router = useRouter<TRouter>()
|
|
99
109
|
const nearestMatchId = React.useContext(
|
|
100
110
|
opts.from ? dummyMatchContext : matchContext,
|
|
101
111
|
)
|
|
102
112
|
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
113
|
+
const key = opts.from ?? nearestMatchId
|
|
114
|
+
const matchStore = key
|
|
115
|
+
? opts.from
|
|
116
|
+
? router.stores.getMatchStoreByRouteId(key)
|
|
117
|
+
: router.stores.activeMatchStoresById.get(key)
|
|
118
|
+
: undefined
|
|
119
|
+
|
|
120
|
+
if (isServer ?? router.isServer) {
|
|
121
|
+
const match = matchStore?.state
|
|
122
|
+
invariant(
|
|
123
|
+
!((opts.shouldThrow ?? true) && !match),
|
|
124
|
+
`Could not find ${opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
if (match === undefined) {
|
|
128
|
+
return undefined as any
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return (opts.select ? opts.select(match as any) : match) as any
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const previousResult =
|
|
135
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
|
|
136
|
+
React.useRef<ValidateSelected<TRouter, TSelected, TStructuralSharing>>(
|
|
137
|
+
undefined,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
|
|
141
|
+
return useStore(matchStore ?? dummyStore, (match) => {
|
|
142
|
+
invariant(
|
|
143
|
+
!((opts.shouldThrow ?? true) && !match),
|
|
144
|
+
`Could not find ${opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
if (match === undefined) {
|
|
148
|
+
return undefined
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const selected = (
|
|
152
|
+
opts.select ? opts.select(match as any) : match
|
|
153
|
+
) as ValidateSelected<TRouter, TSelected, TStructuralSharing>
|
|
154
|
+
|
|
155
|
+
if (opts.structuralSharing ?? router.options.defaultStructuralSharing) {
|
|
156
|
+
const shared = replaceEqualDeep(previousResult.current, selected)
|
|
157
|
+
previousResult.current = shared
|
|
158
|
+
return shared
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return selected
|
|
162
|
+
}) as any
|
|
123
163
|
}
|
package/src/useRouterState.tsx
CHANGED
|
@@ -57,7 +57,9 @@ export function useRouterState<
|
|
|
57
57
|
// Avoid subscribing to the store (and any structural sharing work) on the server.
|
|
58
58
|
const _isServer = isServer ?? router.isServer
|
|
59
59
|
if (_isServer) {
|
|
60
|
-
const state = router.state as RouterState<
|
|
60
|
+
const state = router.stores.__store.state as RouterState<
|
|
61
|
+
TRouter['routeTree']
|
|
62
|
+
>
|
|
61
63
|
return (opts?.select ? opts.select(state) : state) as UseRouterStateResult<
|
|
62
64
|
TRouter,
|
|
63
65
|
TSelected
|
|
@@ -69,7 +71,7 @@ export function useRouterState<
|
|
|
69
71
|
useRef<ValidateSelected<TRouter, TSelected, TStructuralSharing>>(undefined)
|
|
70
72
|
|
|
71
73
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
72
|
-
return useStore(router.__store, (state) => {
|
|
74
|
+
return useStore(router.stores.__store, (state) => {
|
|
73
75
|
if (opts?.select) {
|
|
74
76
|
if (opts.structuralSharing ?? router.options.defaultStructuralSharing) {
|
|
75
77
|
const newSlice = replaceEqualDeep(
|