@tanstack/react-router 1.31.22 → 1.31.23
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/Matches.cjs +9 -2
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/RouterProvider.cjs +4 -120
- package/dist/cjs/RouterProvider.cjs.map +1 -1
- package/dist/cjs/Transitioner.cjs +110 -0
- package/dist/cjs/Transitioner.cjs.map +1 -0
- package/dist/cjs/Transitioner.d.cts +1 -0
- package/dist/cjs/utils.cjs +11 -0
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +1 -0
- package/dist/esm/Matches.js +11 -4
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/RouterProvider.js +4 -103
- package/dist/esm/RouterProvider.js.map +1 -1
- package/dist/esm/Transitioner.d.ts +1 -0
- package/dist/esm/Transitioner.js +93 -0
- package/dist/esm/Transitioner.js.map +1 -0
- package/dist/esm/utils.d.ts +1 -0
- package/dist/esm/utils.js +11 -0
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/Matches.tsx +17 -7
- package/src/RouterProvider.tsx +6 -149
- package/src/Transitioner.tsx +126 -0
- package/src/utils.ts +12 -0
package/src/RouterProvider.tsx
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
1
2
|
import * as React from 'react'
|
|
2
|
-
import { flushSync } from 'react-dom'
|
|
3
3
|
import { Matches } from './Matches'
|
|
4
|
-
import { pick, useLayoutEffect } from './utils'
|
|
5
|
-
import { useRouter } from './useRouter'
|
|
6
|
-
import { useRouterState } from './useRouterState'
|
|
7
4
|
import { getRouterContext } from './routerContext'
|
|
5
|
+
import { Transitioner } from './Transitioner'
|
|
8
6
|
import type { NavigateOptions, ToOptions } from './link'
|
|
9
7
|
import type { ParsedLocation } from './location'
|
|
10
8
|
import type { AnyRoute } from './route'
|
|
@@ -87,17 +85,11 @@ export function RouterContextProvider<
|
|
|
87
85
|
|
|
88
86
|
const routerContext = getRouterContext()
|
|
89
87
|
|
|
90
|
-
const pendingElement = router.options.defaultPendingComponent ? (
|
|
91
|
-
<router.options.defaultPendingComponent />
|
|
92
|
-
) : null
|
|
93
|
-
|
|
94
88
|
const provider = (
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
</routerContext.Provider>
|
|
100
|
-
</React.Suspense>
|
|
89
|
+
<routerContext.Provider value={router}>
|
|
90
|
+
{children}
|
|
91
|
+
<Transitioner />
|
|
92
|
+
</routerContext.Provider>
|
|
101
93
|
)
|
|
102
94
|
|
|
103
95
|
if (router.options.Wrap) {
|
|
@@ -118,129 +110,6 @@ export function RouterProvider<
|
|
|
118
110
|
)
|
|
119
111
|
}
|
|
120
112
|
|
|
121
|
-
function Transitioner() {
|
|
122
|
-
const router = useRouter()
|
|
123
|
-
const mountLoadForRouter = React.useRef({ router, mounted: false })
|
|
124
|
-
const routerState = useRouterState({
|
|
125
|
-
select: (s) =>
|
|
126
|
-
pick(s, ['isLoading', 'location', 'resolvedLocation', 'isTransitioning']),
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
const [isTransitioning, startReactTransition_] = React.useTransition()
|
|
130
|
-
// Track pending state changes
|
|
131
|
-
const hasPendingMatches = useRouterState({
|
|
132
|
-
select: (s) => s.matches.some((d) => d.status === 'pending'),
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
const previousIsLoading = usePrevious(routerState.isLoading)
|
|
136
|
-
|
|
137
|
-
const isAnyPending =
|
|
138
|
-
routerState.isLoading || isTransitioning || hasPendingMatches
|
|
139
|
-
const previousIsAnyPending = usePrevious(isAnyPending)
|
|
140
|
-
|
|
141
|
-
router.startReactTransition = startReactTransition_
|
|
142
|
-
|
|
143
|
-
const tryLoad = async () => {
|
|
144
|
-
try {
|
|
145
|
-
await router.load()
|
|
146
|
-
} catch (err) {
|
|
147
|
-
console.error(err)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Subscribe to location changes
|
|
152
|
-
// and try to load the new location
|
|
153
|
-
useLayoutEffect(() => {
|
|
154
|
-
const unsub = router.history.subscribe(router.load)
|
|
155
|
-
|
|
156
|
-
const nextLocation = router.buildLocation({
|
|
157
|
-
to: router.latestLocation.pathname,
|
|
158
|
-
search: true,
|
|
159
|
-
params: true,
|
|
160
|
-
hash: true,
|
|
161
|
-
state: true,
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
if (routerState.location.href !== nextLocation.href) {
|
|
165
|
-
router.commitLocation({ ...nextLocation, replace: true })
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return () => {
|
|
169
|
-
unsub()
|
|
170
|
-
}
|
|
171
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
172
|
-
}, [router, router.history])
|
|
173
|
-
|
|
174
|
-
// Try to load the initial location
|
|
175
|
-
useLayoutEffect(() => {
|
|
176
|
-
if (
|
|
177
|
-
window.__TSR_DEHYDRATED__ ||
|
|
178
|
-
(mountLoadForRouter.current.router === router &&
|
|
179
|
-
mountLoadForRouter.current.mounted)
|
|
180
|
-
) {
|
|
181
|
-
return
|
|
182
|
-
}
|
|
183
|
-
mountLoadForRouter.current = { router, mounted: true }
|
|
184
|
-
tryLoad()
|
|
185
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
186
|
-
}, [router])
|
|
187
|
-
|
|
188
|
-
useLayoutEffect(() => {
|
|
189
|
-
// The router was loading and now it's not
|
|
190
|
-
if (previousIsLoading && !routerState.isLoading) {
|
|
191
|
-
const toLocation = router.state.location
|
|
192
|
-
const fromLocation = router.state.resolvedLocation
|
|
193
|
-
const pathChanged = fromLocation.href !== toLocation.href
|
|
194
|
-
|
|
195
|
-
router.emit({
|
|
196
|
-
type: 'onLoad',
|
|
197
|
-
fromLocation,
|
|
198
|
-
toLocation,
|
|
199
|
-
pathChanged,
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
// if (router.viewTransitionPromise) {
|
|
203
|
-
// console.log('resolving view transition promise')
|
|
204
|
-
// }
|
|
205
|
-
|
|
206
|
-
// router.viewTransitionPromise?.resolve(true)
|
|
207
|
-
}
|
|
208
|
-
}, [previousIsLoading, router, routerState.isLoading])
|
|
209
|
-
|
|
210
|
-
useLayoutEffect(() => {
|
|
211
|
-
// The router was pending and now it's not
|
|
212
|
-
if (previousIsAnyPending && !isAnyPending) {
|
|
213
|
-
const toLocation = router.state.location
|
|
214
|
-
const fromLocation = router.state.resolvedLocation
|
|
215
|
-
const pathChanged = fromLocation.href !== toLocation.href
|
|
216
|
-
|
|
217
|
-
router.emit({
|
|
218
|
-
type: 'onResolved',
|
|
219
|
-
fromLocation,
|
|
220
|
-
toLocation,
|
|
221
|
-
pathChanged,
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
router.__store.setState((s) => ({
|
|
225
|
-
...s,
|
|
226
|
-
status: 'idle',
|
|
227
|
-
resolvedLocation: s.location,
|
|
228
|
-
}))
|
|
229
|
-
|
|
230
|
-
if ((document as any).querySelector) {
|
|
231
|
-
if (router.state.location.hash !== '') {
|
|
232
|
-
const el = document.getElementById(router.state.location.hash)
|
|
233
|
-
if (el) {
|
|
234
|
-
el.scrollIntoView()
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}, [isAnyPending, previousIsAnyPending, router])
|
|
240
|
-
|
|
241
|
-
return null
|
|
242
|
-
}
|
|
243
|
-
|
|
244
113
|
export function getRouteMatch<TRouteTree extends AnyRoute>(
|
|
245
114
|
state: RouterState<TRouteTree>,
|
|
246
115
|
id: string,
|
|
@@ -275,15 +144,3 @@ export type RouterProps<
|
|
|
275
144
|
>['context']
|
|
276
145
|
>
|
|
277
146
|
}
|
|
278
|
-
|
|
279
|
-
function usePrevious<T>(value: T): T {
|
|
280
|
-
const ref = React.useRef<T>(value)
|
|
281
|
-
|
|
282
|
-
if (ref.current !== value) {
|
|
283
|
-
const prevValue = ref.current
|
|
284
|
-
ref.current = value
|
|
285
|
-
return prevValue
|
|
286
|
-
} else {
|
|
287
|
-
return ref.current
|
|
288
|
-
}
|
|
289
|
-
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { pick, useLayoutEffect, usePrevious } from './utils'
|
|
3
|
+
import { useRouter } from './useRouter'
|
|
4
|
+
import { useRouterState } from './useRouterState'
|
|
5
|
+
|
|
6
|
+
export function Transitioner() {
|
|
7
|
+
const router = useRouter()
|
|
8
|
+
const mountLoadForRouter = React.useRef({ router, mounted: false })
|
|
9
|
+
const routerState = useRouterState({
|
|
10
|
+
select: (s) =>
|
|
11
|
+
pick(s, ['isLoading', 'location', 'resolvedLocation', 'isTransitioning']),
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const [isTransitioning, startReactTransition_] = React.useTransition()
|
|
15
|
+
// Track pending state changes
|
|
16
|
+
const hasPendingMatches = useRouterState({
|
|
17
|
+
select: (s) => s.matches.some((d) => d.status === 'pending'),
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const previousIsLoading = usePrevious(routerState.isLoading)
|
|
21
|
+
|
|
22
|
+
const isAnyPending =
|
|
23
|
+
routerState.isLoading || isTransitioning || hasPendingMatches
|
|
24
|
+
const previousIsAnyPending = usePrevious(isAnyPending)
|
|
25
|
+
|
|
26
|
+
router.startReactTransition = startReactTransition_
|
|
27
|
+
|
|
28
|
+
const tryLoad = async () => {
|
|
29
|
+
try {
|
|
30
|
+
await router.load()
|
|
31
|
+
} catch (err) {
|
|
32
|
+
console.error(err)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Subscribe to location changes
|
|
37
|
+
// and try to load the new location
|
|
38
|
+
useLayoutEffect(() => {
|
|
39
|
+
const unsub = router.history.subscribe(router.load)
|
|
40
|
+
|
|
41
|
+
const nextLocation = router.buildLocation({
|
|
42
|
+
to: router.latestLocation.pathname,
|
|
43
|
+
search: true,
|
|
44
|
+
params: true,
|
|
45
|
+
hash: true,
|
|
46
|
+
state: true,
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
if (routerState.location.href !== nextLocation.href) {
|
|
50
|
+
router.commitLocation({ ...nextLocation, replace: true })
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return () => {
|
|
54
|
+
unsub()
|
|
55
|
+
}
|
|
56
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
57
|
+
}, [router, router.history])
|
|
58
|
+
|
|
59
|
+
// Try to load the initial location
|
|
60
|
+
useLayoutEffect(() => {
|
|
61
|
+
if (
|
|
62
|
+
window.__TSR_DEHYDRATED__ ||
|
|
63
|
+
(mountLoadForRouter.current.router === router &&
|
|
64
|
+
mountLoadForRouter.current.mounted)
|
|
65
|
+
) {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
mountLoadForRouter.current = { router, mounted: true }
|
|
69
|
+
tryLoad()
|
|
70
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
71
|
+
}, [router])
|
|
72
|
+
|
|
73
|
+
useLayoutEffect(() => {
|
|
74
|
+
// The router was loading and now it's not
|
|
75
|
+
if (previousIsLoading && !routerState.isLoading) {
|
|
76
|
+
const toLocation = router.state.location
|
|
77
|
+
const fromLocation = router.state.resolvedLocation
|
|
78
|
+
const pathChanged = fromLocation.href !== toLocation.href
|
|
79
|
+
|
|
80
|
+
router.emit({
|
|
81
|
+
type: 'onLoad',
|
|
82
|
+
fromLocation,
|
|
83
|
+
toLocation,
|
|
84
|
+
pathChanged,
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// if (router.viewTransitionPromise) {
|
|
88
|
+
// console.log('resolving view transition promise')
|
|
89
|
+
// }
|
|
90
|
+
// router.viewTransitionPromise?.resolve(true)
|
|
91
|
+
}
|
|
92
|
+
}, [previousIsLoading, router, routerState.isLoading])
|
|
93
|
+
|
|
94
|
+
useLayoutEffect(() => {
|
|
95
|
+
// The router was pending and now it's not
|
|
96
|
+
if (previousIsAnyPending && !isAnyPending) {
|
|
97
|
+
const toLocation = router.state.location
|
|
98
|
+
const fromLocation = router.state.resolvedLocation
|
|
99
|
+
const pathChanged = fromLocation.href !== toLocation.href
|
|
100
|
+
|
|
101
|
+
router.emit({
|
|
102
|
+
type: 'onResolved',
|
|
103
|
+
fromLocation,
|
|
104
|
+
toLocation,
|
|
105
|
+
pathChanged,
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
router.__store.setState((s) => ({
|
|
109
|
+
...s,
|
|
110
|
+
status: 'idle',
|
|
111
|
+
resolvedLocation: s.location,
|
|
112
|
+
}))
|
|
113
|
+
|
|
114
|
+
if ((document as any).querySelector) {
|
|
115
|
+
if (router.state.location.hash !== '') {
|
|
116
|
+
const el = document.getElementById(router.state.location.hash)
|
|
117
|
+
if (el) {
|
|
118
|
+
el.scrollIntoView()
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}, [isAnyPending, previousIsAnyPending, router])
|
|
124
|
+
|
|
125
|
+
return null
|
|
126
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -352,3 +352,15 @@ export function removeLayoutSegments(routePath: string): string {
|
|
|
352
352
|
const newSegments = segments.filter((segment) => !segment.startsWith('_'))
|
|
353
353
|
return newSegments.join('/')
|
|
354
354
|
}
|
|
355
|
+
|
|
356
|
+
export function usePrevious<T>(value: T): T {
|
|
357
|
+
const ref = React.useRef<T>(value)
|
|
358
|
+
|
|
359
|
+
if (ref.current !== value) {
|
|
360
|
+
const prevValue = ref.current
|
|
361
|
+
ref.current = value
|
|
362
|
+
return prevValue
|
|
363
|
+
} else {
|
|
364
|
+
return ref.current
|
|
365
|
+
}
|
|
366
|
+
}
|