-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathuseMatch.tsx
More file actions
134 lines (118 loc) 路 3.56 KB
/
Copy pathuseMatch.tsx
File metadata and controls
134 lines (118 loc) 路 3.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import * as Solid from 'solid-js'
import { invariant, replaceEqualDeep } from '@tanstack/router-core'
import { nearestMatchContext } from './matchContext'
import { useRouter } from './useRouter'
import type {
AnyRouter,
MakeRouteMatch,
MakeRouteMatchUnion,
RegisteredRouter,
StrictOrFrom,
ThrowConstraint,
ThrowOrOptional,
} from '@tanstack/router-core'
export interface UseMatchBaseOptions<
TRouter extends AnyRouter,
TFrom,
TStrict extends boolean,
TThrow extends boolean,
TSelected,
> {
select?: (
match: MakeRouteMatch<TRouter['routeTree'], TFrom, TStrict>,
) => TSelected
shouldThrow?: TThrow
}
export type UseMatchRoute<out TFrom> = <
TRouter extends AnyRouter = RegisteredRouter,
TSelected = unknown,
>(
opts?: UseMatchBaseOptions<TRouter, TFrom, true, true, TSelected>,
) => Solid.Accessor<UseMatchResult<TRouter, TFrom, true, TSelected>>
export type UseMatchOptions<
TRouter extends AnyRouter,
TFrom extends string | undefined,
TStrict extends boolean,
TThrow extends boolean,
TSelected,
> = StrictOrFrom<TRouter, TFrom, TStrict> &
UseMatchBaseOptions<TRouter, TFrom, TStrict, TThrow, TSelected>
export type UseMatchResult<
TRouter extends AnyRouter,
TFrom,
TStrict extends boolean,
TSelected,
> = unknown extends TSelected
? TStrict extends true
? MakeRouteMatch<TRouter['routeTree'], TFrom, TStrict>
: MakeRouteMatchUnion<TRouter>
: TSelected
export function useMatch<
TRouter extends AnyRouter = RegisteredRouter,
const TFrom extends string | undefined = undefined,
TStrict extends boolean = true,
TThrow extends boolean = true,
TSelected = unknown,
>(
opts: UseMatchOptions<
TRouter,
TFrom,
TStrict,
ThrowConstraint<TStrict, TThrow>,
TSelected
>,
): Solid.Accessor<
ThrowOrOptional<UseMatchResult<TRouter, TFrom, TStrict, TSelected>, TThrow>
> {
const router = useRouter<TRouter>()
const nearestMatch = opts.from
? undefined
: Solid.useContext(nearestMatchContext)
const match = () => {
if (opts.from) {
return router.stores.matches
.get()
.find((match) => match.routeId === opts.from)
}
return nearestMatch?.match()
}
// The returned accessor can be read after the owning scope has been
// disposed (e.g. async work started by a route component that resolves
// after navigating away). Once disposed, keep returning the last known
// value instead of throwing on the now-missing match.
let isDisposed = false
if (Solid.getOwner()) {
Solid.onCleanup(() => {
isDisposed = true
})
}
return Solid.createMemo((prev: TSelected | undefined) => {
const selectedMatch = match()
if (selectedMatch === undefined) {
if (prev !== undefined && isDisposed) {
return prev
}
const hasPendingMatch = opts.from
? Boolean(router.stores.pendingRouteIds.get()[opts.from!])
: (nearestMatch?.hasPending() ?? false)
if (
prev !== undefined &&
(hasPendingMatch || router.stores.isTransitioning.get())
) {
return prev
}
if (!hasPendingMatch && (opts.shouldThrow ?? true)) {
if (process.env.NODE_ENV !== 'production') {
throw new Error(
`Invariant failed: Could not find ${opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`,
)
}
invariant()
}
return undefined
}
const res = opts.select ? opts.select(selectedMatch as any) : selectedMatch
if (prev === undefined) return res as TSelected
return replaceEqualDeep(prev, res) as TSelected
}) as any
}