@tanstack/react-router 0.0.1-beta.9 → 1.0.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.
Files changed (106) hide show
  1. package/LICENSE +21 -0
  2. package/build/cjs/CatchBoundary.js +128 -0
  3. package/build/cjs/CatchBoundary.js.map +1 -0
  4. package/build/cjs/Matches.js +233 -0
  5. package/build/cjs/Matches.js.map +1 -0
  6. package/build/cjs/RouterProvider.js +172 -0
  7. package/build/cjs/RouterProvider.js.map +1 -0
  8. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +2 -22
  9. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
  10. package/build/cjs/awaited.js +43 -0
  11. package/build/cjs/awaited.js.map +1 -0
  12. package/build/cjs/defer.js +37 -0
  13. package/build/cjs/defer.js.map +1 -0
  14. package/build/cjs/fileRoute.js +27 -0
  15. package/build/cjs/fileRoute.js.map +1 -0
  16. package/build/cjs/index.js +130 -0
  17. package/build/cjs/index.js.map +1 -0
  18. package/build/cjs/lazyRouteComponent.js +54 -0
  19. package/build/cjs/lazyRouteComponent.js.map +1 -0
  20. package/build/cjs/link.js +223 -0
  21. package/build/cjs/link.js.map +1 -0
  22. package/build/cjs/path.js +214 -0
  23. package/build/cjs/path.js.map +1 -0
  24. package/build/cjs/qss.js +63 -0
  25. package/build/cjs/qss.js.map +1 -0
  26. package/build/cjs/redirects.js +28 -0
  27. package/build/cjs/redirects.js.map +1 -0
  28. package/build/cjs/route.js +191 -0
  29. package/build/cjs/route.js.map +1 -0
  30. package/build/cjs/router.js +1085 -0
  31. package/build/cjs/router.js.map +1 -0
  32. package/build/cjs/scroll-restoration.js +202 -0
  33. package/build/cjs/scroll-restoration.js.map +1 -0
  34. package/build/cjs/searchParams.js +81 -0
  35. package/build/cjs/searchParams.js.map +1 -0
  36. package/build/cjs/useBlocker.js +55 -0
  37. package/build/cjs/useBlocker.js.map +1 -0
  38. package/build/cjs/useNavigate.js +86 -0
  39. package/build/cjs/useNavigate.js.map +1 -0
  40. package/build/cjs/useParams.js +26 -0
  41. package/build/cjs/useParams.js.map +1 -0
  42. package/build/cjs/useSearch.js +25 -0
  43. package/build/cjs/useSearch.js.map +1 -0
  44. package/build/cjs/utils.js +241 -0
  45. package/build/cjs/utils.js.map +1 -0
  46. package/build/esm/index.js +2302 -2534
  47. package/build/esm/index.js.map +1 -1
  48. package/build/stats-html.html +3498 -2694
  49. package/build/stats-react.json +1204 -44
  50. package/build/types/CatchBoundary.d.ts +36 -0
  51. package/build/types/Matches.d.ts +64 -0
  52. package/build/types/RouterProvider.d.ts +35 -0
  53. package/build/types/awaited.d.ts +9 -0
  54. package/build/types/defer.d.ts +19 -0
  55. package/build/types/fileRoute.d.ts +38 -0
  56. package/build/types/history.d.ts +7 -0
  57. package/build/types/index.d.ts +27 -74
  58. package/build/types/lazyRouteComponent.d.ts +2 -0
  59. package/build/types/link.d.ts +93 -0
  60. package/build/types/location.d.ts +12 -0
  61. package/build/types/path.d.ts +17 -0
  62. package/build/types/qss.d.ts +2 -0
  63. package/build/types/redirects.d.ts +11 -0
  64. package/build/types/route.d.ts +283 -0
  65. package/build/types/routeInfo.d.ts +31 -0
  66. package/build/types/router.d.ts +186 -0
  67. package/build/types/scroll-restoration.d.ts +18 -0
  68. package/build/types/searchParams.d.ts +7 -0
  69. package/build/types/useBlocker.d.ts +9 -0
  70. package/build/types/useNavigate.d.ts +19 -0
  71. package/build/types/useParams.d.ts +7 -0
  72. package/build/types/useSearch.d.ts +7 -0
  73. package/build/types/utils.d.ts +69 -0
  74. package/build/umd/index.development.js +2899 -2493
  75. package/build/umd/index.development.js.map +1 -1
  76. package/build/umd/index.production.js +4 -4
  77. package/build/umd/index.production.js.map +1 -1
  78. package/package.json +12 -10
  79. package/src/CatchBoundary.tsx +101 -0
  80. package/src/Matches.tsx +423 -0
  81. package/src/RouterProvider.tsx +254 -0
  82. package/src/awaited.tsx +40 -0
  83. package/src/defer.ts +55 -0
  84. package/src/fileRoute.ts +152 -0
  85. package/src/history.ts +8 -0
  86. package/src/index.tsx +28 -619
  87. package/src/lazyRouteComponent.tsx +33 -0
  88. package/src/link.tsx +603 -0
  89. package/src/location.ts +13 -0
  90. package/src/path.ts +261 -0
  91. package/src/qss.ts +53 -0
  92. package/src/redirects.ts +39 -0
  93. package/src/route.ts +882 -0
  94. package/src/routeInfo.ts +84 -0
  95. package/src/router.ts +1671 -0
  96. package/src/scroll-restoration.tsx +230 -0
  97. package/src/searchParams.ts +79 -0
  98. package/src/useBlocker.tsx +27 -0
  99. package/src/useNavigate.tsx +111 -0
  100. package/src/useParams.tsx +25 -0
  101. package/src/useSearch.tsx +25 -0
  102. package/src/utils.ts +360 -0
  103. package/build/cjs/react-router/src/index.js +0 -458
  104. package/build/cjs/react-router/src/index.js.map +0 -1
  105. package/build/cjs/router-core/build/esm/index.js +0 -2524
  106. package/build/cjs/router-core/build/esm/index.js.map +0 -1
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
3
  "author": "Tanner Linsley",
4
- "version": "0.0.1-beta.9",
4
+ "version": "1.0.1",
5
5
  "license": "MIT",
6
6
  "repository": "tanstack/router",
7
- "homepage": "https://tanstack.com/router/",
7
+ "homepage": "https://tanstack.com/router",
8
8
  "description": "",
9
9
  "publishConfig": {
10
10
  "registry": "https://registry.npmjs.org/"
@@ -12,7 +12,6 @@
12
12
  "keywords": [
13
13
  "react",
14
14
  "location",
15
- "@tanstack/react-router",
16
15
  "router",
17
16
  "routing",
18
17
  "async",
@@ -24,7 +23,7 @@
24
23
  "url": "https://github.com/sponsors/tannerlinsley"
25
24
  },
26
25
  "module": "build/esm/index.js",
27
- "main": "build/cjs/react-router/src/index.js",
26
+ "main": "build/cjs/index.js",
28
27
  "browser": "build/umd/index.production.js",
29
28
  "types": "build/types/index.d.ts",
30
29
  "engines": {
@@ -34,17 +33,20 @@
34
33
  "build/**",
35
34
  "src"
36
35
  ],
36
+ "sideEffects": false,
37
37
  "peerDependencies": {
38
38
  "react": ">=16",
39
39
  "react-dom": ">=16"
40
40
  },
41
41
  "dependencies": {
42
42
  "@babel/runtime": "^7.16.7",
43
- "@tanstack/router-core": "0.0.1-beta.9",
44
- "use-sync-external-store": "^1.2.0"
43
+ "@tanstack/react-store": "^0.2.1",
44
+ "@tanstack/store": "^0.1.3",
45
+ "tiny-invariant": "^1.3.1",
46
+ "tiny-warning": "^1.0.3",
47
+ "@tanstack/history": "1.0.1"
45
48
  },
46
- "devDependencies": {
47
- "@types/use-sync-external-store": "^0.0.3",
48
- "babel-plugin-transform-async-to-promises": "^0.8.18"
49
+ "scripts": {
50
+ "build": "rollup --config rollup.config.js"
49
51
  }
50
- }
52
+ }
@@ -0,0 +1,101 @@
1
+ import * as React from 'react'
2
+
3
+ export function CatchBoundary(props: {
4
+ getResetKey: () => string
5
+ children: any
6
+ errorComponent?: any
7
+ onCatch: (error: any) => void
8
+ }) {
9
+ const errorComponent = props.errorComponent ?? ErrorComponent
10
+
11
+ return (
12
+ <CatchBoundaryImpl
13
+ getResetKey={props.getResetKey}
14
+ onCatch={props.onCatch}
15
+ children={({ error }) => {
16
+ if (error) {
17
+ return React.createElement(errorComponent, {
18
+ error,
19
+ })
20
+ }
21
+
22
+ return props.children
23
+ }}
24
+ />
25
+ )
26
+ }
27
+
28
+ export class CatchBoundaryImpl extends React.Component<{
29
+ getResetKey: () => string
30
+ children: (props: { error: any; reset: () => void }) => any
31
+ onCatch?: (error: any) => void
32
+ }> {
33
+ state = { error: null } as any
34
+ static getDerivedStateFromProps(props: any) {
35
+ return { resetKey: props.getResetKey() }
36
+ }
37
+ static getDerivedStateFromError(error: any) {
38
+ return { error }
39
+ }
40
+ componentDidUpdate(
41
+ prevProps: Readonly<{
42
+ getResetKey: () => string
43
+ children: (props: { error: any; reset: () => void }) => any
44
+ onCatch?: ((error: any, info: any) => void) | undefined
45
+ }>,
46
+ prevState: any,
47
+ ): void {
48
+ if (prevState.error && prevState.resetKey !== this.state.resetKey) {
49
+ this.setState({ error: null })
50
+ }
51
+ }
52
+ componentDidCatch(error: any) {
53
+ console.error(error)
54
+ this.props.onCatch?.(error)
55
+ }
56
+ render() {
57
+ return this.props.children(this.state)
58
+ }
59
+ }
60
+
61
+ export function ErrorComponent({ error }: { error: any }) {
62
+ const [show, setShow] = React.useState(process.env.NODE_ENV !== 'production')
63
+
64
+ return (
65
+ <div style={{ padding: '.5rem', maxWidth: '100%' }}>
66
+ <div style={{ display: 'flex', alignItems: 'center', gap: '.5rem' }}>
67
+ <strong style={{ fontSize: '1rem' }}>Something went wrong!</strong>
68
+ <button
69
+ style={{
70
+ appearance: 'none',
71
+ fontSize: '.6em',
72
+ border: '1px solid currentColor',
73
+ padding: '.1rem .2rem',
74
+ fontWeight: 'bold',
75
+ borderRadius: '.25rem',
76
+ }}
77
+ onClick={() => setShow((d) => !d)}
78
+ >
79
+ {show ? 'Hide Error' : 'Show Error'}
80
+ </button>
81
+ </div>
82
+ <div style={{ height: '.25rem' }} />
83
+ {show ? (
84
+ <div>
85
+ <pre
86
+ style={{
87
+ fontSize: '.7em',
88
+ border: '1px solid red',
89
+ borderRadius: '.25rem',
90
+ padding: '.3rem',
91
+ color: 'red',
92
+ overflow: 'auto',
93
+ }}
94
+ >
95
+ {error.message ? <code>{error.message}</code> : null}
96
+ </pre>
97
+ </div>
98
+ ) : null}
99
+ </div>
100
+ )
101
+ }
@@ -0,0 +1,423 @@
1
+ import * as React from 'react'
2
+ import invariant from 'tiny-invariant'
3
+ import warning from 'tiny-warning'
4
+ import { CatchBoundary, ErrorComponent } from './CatchBoundary'
5
+ import { useRouter, useRouterState } from './RouterProvider'
6
+ import { ResolveRelativePath, ToOptions } from './link'
7
+ import { AnyRoute, ReactNode, rootRouteId } from './route'
8
+ import {
9
+ FullSearchSchema,
10
+ ParseRoute,
11
+ RouteById,
12
+ RouteByPath,
13
+ RouteIds,
14
+ RoutePaths,
15
+ } from './routeInfo'
16
+ import { RegisteredRouter, RouterState } from './router'
17
+ import { NoInfer, StrictOrFrom, pick } from './utils'
18
+
19
+ export const matchContext = React.createContext<string | undefined>(undefined)
20
+
21
+ export interface RouteMatch<
22
+ TRouteTree extends AnyRoute = AnyRoute,
23
+ TRouteId extends RouteIds<TRouteTree> = ParseRoute<TRouteTree>['id'],
24
+ > {
25
+ id: string
26
+ routeId: TRouteId
27
+ pathname: string
28
+ params: RouteById<TRouteTree, TRouteId>['types']['allParams']
29
+ status: 'pending' | 'success' | 'error'
30
+ isFetching: boolean
31
+ showPending: boolean
32
+ error: unknown
33
+ paramsError: unknown
34
+ searchError: unknown
35
+ updatedAt: number
36
+ loadPromise?: Promise<void>
37
+ loaderData?: RouteById<TRouteTree, TRouteId>['types']['loaderData']
38
+ routeContext: RouteById<TRouteTree, TRouteId>['types']['routeContext']
39
+ context: RouteById<TRouteTree, TRouteId>['types']['allContext']
40
+ search: FullSearchSchema<TRouteTree> &
41
+ RouteById<TRouteTree, TRouteId>['types']['fullSearchSchema']
42
+ fetchCount: number
43
+ abortController: AbortController
44
+ cause: 'preload' | 'enter' | 'stay'
45
+ loaderDeps: RouteById<TRouteTree, TRouteId>['types']['loaderDeps']
46
+ preload: boolean
47
+ invalid: boolean
48
+ }
49
+
50
+ export type AnyRouteMatch = RouteMatch<any, any>
51
+
52
+ export function Matches() {
53
+ const router = useRouter()
54
+ const matchId = useRouterState({
55
+ select: (s) => {
56
+ return getRenderedMatches(s)[0]?.id
57
+ },
58
+ })
59
+
60
+ return (
61
+ <matchContext.Provider value={matchId}>
62
+ <CatchBoundary
63
+ getResetKey={() => router.state.resolvedLocation.state?.key}
64
+ errorComponent={ErrorComponent}
65
+ onCatch={() => {
66
+ warning(
67
+ false,
68
+ `Error in router! Consider setting an 'errorComponent' in your RootRoute! 👍`,
69
+ )
70
+ }}
71
+ >
72
+ {matchId ? <Match matchId={matchId} /> : null}
73
+ </CatchBoundary>
74
+ </matchContext.Provider>
75
+ )
76
+ }
77
+
78
+ function SafeFragment(props: any) {
79
+ return <>{props.children}</>
80
+ }
81
+
82
+ export function Match({ matchId }: { matchId: string }) {
83
+ const router = useRouter()
84
+ const routeId = useRouterState({
85
+ select: (s) =>
86
+ getRenderedMatches(s).find((d) => d.id === matchId)?.routeId as string,
87
+ })
88
+
89
+ invariant(
90
+ routeId,
91
+ `Could not find routeId for matchId "${matchId}". Please file an issue!`,
92
+ )
93
+
94
+ const route = router.routesById[routeId]!
95
+
96
+ const PendingComponent = (route.options.pendingComponent ??
97
+ router.options.defaultPendingComponent) as any
98
+
99
+ const pendingElement = PendingComponent ? <PendingComponent /> : null
100
+
101
+ const routeErrorComponent =
102
+ route.options.errorComponent ??
103
+ router.options.defaultErrorComponent ??
104
+ ErrorComponent
105
+
106
+ const ResolvedSuspenseBoundary =
107
+ route.options.wrapInSuspense ??
108
+ PendingComponent ??
109
+ route.options.component?.preload ??
110
+ route.options.pendingComponent?.preload ??
111
+ (route.options.errorComponent as any)?.preload
112
+ ? React.Suspense
113
+ : SafeFragment
114
+
115
+ const ResolvedCatchBoundary = routeErrorComponent
116
+ ? CatchBoundary
117
+ : SafeFragment
118
+
119
+ return (
120
+ <matchContext.Provider value={matchId}>
121
+ <ResolvedSuspenseBoundary fallback={pendingElement}>
122
+ <ResolvedCatchBoundary
123
+ getResetKey={() => router.state.resolvedLocation.state?.key}
124
+ errorComponent={routeErrorComponent}
125
+ onCatch={() => {
126
+ warning(false, `Error in route match: ${matchId}`)
127
+ }}
128
+ >
129
+ <MatchInner matchId={matchId!} pendingElement={pendingElement} />
130
+ </ResolvedCatchBoundary>
131
+ </ResolvedSuspenseBoundary>
132
+ </matchContext.Provider>
133
+ )
134
+ }
135
+
136
+ function MatchInner({
137
+ matchId,
138
+ pendingElement,
139
+ }: {
140
+ matchId: string
141
+ pendingElement: any
142
+ }): any {
143
+ const router = useRouter()
144
+ const routeId = useRouterState({
145
+ select: (s) =>
146
+ getRenderedMatches(s).find((d) => d.id === matchId)?.routeId as string,
147
+ })
148
+
149
+ const route = router.routesById[routeId]!
150
+
151
+ const match = useRouterState({
152
+ select: (s) =>
153
+ pick(getRenderedMatches(s).find((d) => d.id === matchId)!, [
154
+ 'status',
155
+ 'error',
156
+ 'showPending',
157
+ 'loadPromise',
158
+ ]),
159
+ })
160
+
161
+ if (match.status === 'error') {
162
+ throw match.error
163
+ }
164
+
165
+ if (match.status === 'pending') {
166
+ if (match.showPending) {
167
+ return pendingElement
168
+ }
169
+ throw match.loadPromise
170
+ }
171
+
172
+ if (match.status === 'success') {
173
+ let Comp = route.options.component ?? router.options.defaultComponent
174
+
175
+ if (Comp) {
176
+ return <Comp />
177
+ }
178
+
179
+ return <Outlet />
180
+ }
181
+
182
+ invariant(
183
+ false,
184
+ 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!',
185
+ )
186
+ }
187
+
188
+ export const Outlet = React.memo(function Outlet() {
189
+ const matchId = React.useContext(matchContext)
190
+
191
+ const childMatchId = useRouterState({
192
+ select: (s) => {
193
+ const matches = getRenderedMatches(s)
194
+ const index = matches.findIndex((d) => d.id === matchId)
195
+ return matches[index + 1]?.id
196
+ },
197
+ })
198
+
199
+ if (!childMatchId) {
200
+ return null
201
+ }
202
+
203
+ return <Match matchId={childMatchId} />
204
+ })
205
+
206
+ export interface MatchRouteOptions {
207
+ pending?: boolean
208
+ caseSensitive?: boolean
209
+ includeSearch?: boolean
210
+ fuzzy?: boolean
211
+ }
212
+
213
+ export type UseMatchRouteOptions<
214
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
215
+ TFrom extends RoutePaths<TRouteTree> = '/',
216
+ TTo extends string = '',
217
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
218
+ TMaskTo extends string = '',
219
+ > = ToOptions<AnyRoute, TFrom, TTo, TMaskFrom, TMaskTo> & MatchRouteOptions
220
+
221
+ export function useMatchRoute<
222
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
223
+ >() {
224
+ useRouterState({ select: (s) => [s.location, s.resolvedLocation] })
225
+ const { matchRoute } = useRouter()
226
+
227
+ return React.useCallback(
228
+ <
229
+ TFrom extends RoutePaths<TRouteTree> = '/',
230
+ TTo extends string = '',
231
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
232
+ TMaskTo extends string = '',
233
+ TResolved extends string = ResolveRelativePath<TFrom, NoInfer<TTo>>,
234
+ >(
235
+ opts: UseMatchRouteOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
236
+ ): false | RouteById<TRouteTree, TResolved>['types']['allParams'] => {
237
+ const { pending, caseSensitive, ...rest } = opts
238
+
239
+ return matchRoute(rest as any, {
240
+ pending,
241
+ caseSensitive,
242
+ })
243
+ },
244
+ [],
245
+ )
246
+ }
247
+
248
+ export type MakeMatchRouteOptions<
249
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
250
+ TFrom extends RoutePaths<TRouteTree> = '/',
251
+ TTo extends string = '',
252
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
253
+ TMaskTo extends string = '',
254
+ > = ToOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
255
+ MatchRouteOptions & {
256
+ // If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
257
+ children?:
258
+ | ((
259
+ params?: RouteByPath<
260
+ TRouteTree,
261
+ ResolveRelativePath<TFrom, NoInfer<TTo>>
262
+ >['types']['allParams'],
263
+ ) => ReactNode)
264
+ | React.ReactNode
265
+ }
266
+
267
+ export function MatchRoute<
268
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
269
+ TFrom extends RoutePaths<TRouteTree> = '/',
270
+ TTo extends string = '',
271
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
272
+ TMaskTo extends string = '',
273
+ >(
274
+ props: MakeMatchRouteOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
275
+ ): any {
276
+ const matchRoute = useMatchRoute()
277
+ const params = matchRoute(props as any)
278
+
279
+ if (typeof props.children === 'function') {
280
+ return (props.children as any)(params)
281
+ }
282
+
283
+ return !!params ? props.children : null
284
+ }
285
+
286
+ function getRenderedMatches(state: RouterState) {
287
+ return state.pendingMatches?.some((d) => d.showPending)
288
+ ? state.pendingMatches
289
+ : state.matches
290
+ }
291
+
292
+ export function useMatch<
293
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
294
+ TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
295
+ TStrict extends boolean = true,
296
+ TRouteMatchState = RouteMatch<TRouteTree, TFrom>,
297
+ TSelected = TRouteMatchState,
298
+ >(
299
+ opts: StrictOrFrom<TFrom> & {
300
+ select?: (match: TRouteMatchState) => TSelected
301
+ },
302
+ ): TStrict extends true ? TSelected : TSelected | undefined {
303
+ const router = useRouter()
304
+ const nearestMatchId = React.useContext(matchContext)
305
+
306
+ const nearestMatchRouteId = getRenderedMatches(router.state).find(
307
+ (d) => d.id === nearestMatchId,
308
+ )?.routeId
309
+
310
+ const matchRouteId = (() => {
311
+ const matches = getRenderedMatches(router.state)
312
+ const match = opts?.from
313
+ ? matches.find((d) => d.routeId === opts?.from)
314
+ : matches.find((d) => d.id === nearestMatchId)
315
+ return match!.routeId
316
+ })()
317
+
318
+ if (opts?.strict ?? true) {
319
+ invariant(
320
+ nearestMatchRouteId == matchRouteId,
321
+ `useMatch("${
322
+ matchRouteId as string
323
+ }") is being called in a component that is meant to render the '${nearestMatchRouteId}' route. Did you mean to 'useMatch("${
324
+ matchRouteId as string
325
+ }", { strict: false })' or 'useRoute("${
326
+ matchRouteId as string
327
+ }")' instead?`,
328
+ )
329
+ }
330
+
331
+ const matchSelection = useRouterState({
332
+ select: (state) => {
333
+ const match = getRenderedMatches(state).find(
334
+ (d) => d.id === nearestMatchId,
335
+ )
336
+
337
+ invariant(
338
+ match,
339
+ `Could not find ${
340
+ opts?.from
341
+ ? `an active match from "${opts.from}"`
342
+ : 'a nearest match!'
343
+ }`,
344
+ )
345
+
346
+ return opts?.select ? opts.select(match as any) : match
347
+ },
348
+ })
349
+
350
+ return matchSelection as any
351
+ }
352
+
353
+ export function useMatches<T = RouteMatch[]>(opts?: {
354
+ select?: (matches: RouteMatch[]) => T
355
+ }): T {
356
+ return useRouterState({
357
+ select: (state) => {
358
+ let matches = getRenderedMatches(state)
359
+ return opts?.select ? opts.select(matches) : (matches as T)
360
+ },
361
+ })
362
+ }
363
+
364
+ export function useParentMatches<T = RouteMatch[]>(opts?: {
365
+ select?: (matches: RouteMatch[]) => T
366
+ }): T {
367
+ const contextMatchId = React.useContext(matchContext)
368
+
369
+ return useMatches({
370
+ select: (matches) => {
371
+ matches = matches.slice(matches.findIndex((d) => d.id === contextMatchId))
372
+ return opts?.select ? opts.select(matches) : (matches as T)
373
+ },
374
+ })
375
+ }
376
+
377
+ export function useLoaderDeps<
378
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
379
+ TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
380
+ TStrict extends boolean = true,
381
+ TRouteMatch extends RouteMatch<TRouteTree, TFrom> = RouteMatch<
382
+ TRouteTree,
383
+ TFrom
384
+ >,
385
+ TSelected = Required<TRouteMatch>['loaderDeps'],
386
+ >(
387
+ opts: StrictOrFrom<TFrom> & {
388
+ select?: (match: TRouteMatch) => TSelected
389
+ },
390
+ ): TStrict extends true ? TSelected : TSelected | undefined {
391
+ return useMatch({
392
+ ...opts,
393
+ select: (s) => {
394
+ return typeof opts.select === 'function'
395
+ ? opts.select(s?.loaderDeps)
396
+ : s?.loaderDeps
397
+ },
398
+ })!
399
+ }
400
+
401
+ export function useLoaderData<
402
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
403
+ TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
404
+ TStrict extends boolean = true,
405
+ TRouteMatch extends RouteMatch<TRouteTree, TFrom> = RouteMatch<
406
+ TRouteTree,
407
+ TFrom
408
+ >,
409
+ TSelected = Required<TRouteMatch>['loaderData'],
410
+ >(
411
+ opts: StrictOrFrom<TFrom> & {
412
+ select?: (match: TRouteMatch) => TSelected
413
+ },
414
+ ): TStrict extends true ? TSelected : TSelected | undefined {
415
+ return useMatch({
416
+ ...opts,
417
+ select: (s) => {
418
+ return typeof opts.select === 'function'
419
+ ? opts.select(s?.loaderData)
420
+ : s?.loaderData
421
+ },
422
+ })!
423
+ }