@tanstack/react-router 1.31.22 → 1.31.24

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/src/Matches.tsx CHANGED
@@ -8,6 +8,7 @@ import { createControlledPromise, pick } from './utils'
8
8
  import { CatchNotFound, DefaultGlobalNotFound, isNotFound } from './not-found'
9
9
  import { isRedirect } from './redirects'
10
10
  import { type AnyRouter, type RegisteredRouter } from './router'
11
+ import { Transitioner } from './Transitioner'
11
12
  import type { ResolveRelativePath, ToOptions } from './link'
12
13
  import type { AnyRoute, ReactNode, StaticDataRouteOption } from './route'
13
14
  import type {
@@ -99,6 +100,25 @@ export type AnyRouteMatch = RouteMatch<any, any, any, any, any, any, any>
99
100
  export function Matches() {
100
101
  const router = useRouter()
101
102
 
103
+ const pendingElement = router.options.defaultPendingComponent ? (
104
+ <router.options.defaultPendingComponent />
105
+ ) : null
106
+
107
+ const inner = (
108
+ <React.Suspense fallback={pendingElement}>
109
+ <Transitioner />
110
+ <MatchesInner />
111
+ </React.Suspense>
112
+ )
113
+
114
+ return router.options.InnerWrap ? (
115
+ <router.options.InnerWrap>{inner}</router.options.InnerWrap>
116
+ ) : (
117
+ inner
118
+ )
119
+ }
120
+
121
+ function MatchesInner() {
102
122
  const matchId = useRouterState({
103
123
  select: (s) => {
104
124
  return s.matches[0]?.id
@@ -109,7 +129,7 @@ export function Matches() {
109
129
  select: (s) => s.resolvedLocation.state.key!,
110
130
  })
111
131
 
112
- const inner = (
132
+ return (
113
133
  <matchContext.Provider value={matchId}>
114
134
  <CatchBoundary
115
135
  getResetKey={() => resetKey}
@@ -126,12 +146,6 @@ export function Matches() {
126
146
  </CatchBoundary>
127
147
  </matchContext.Provider>
128
148
  )
129
-
130
- return router.options.InnerWrap ? (
131
- <router.options.InnerWrap>{inner}</router.options.InnerWrap>
132
- ) : (
133
- inner
134
- )
135
149
  }
136
150
 
137
151
  function SafeFragment(props: any) {
@@ -221,13 +235,7 @@ export function Match({ matchId }: { matchId: string }) {
221
235
  )
222
236
  }
223
237
 
224
- function MatchInner({
225
- matchId,
226
- // pendingElement,
227
- }: {
228
- matchId: string
229
- // pendingElement: any
230
- }): any {
238
+ function MatchInner({ matchId }: { matchId: string }): any {
231
239
  const router = useRouter()
232
240
  const routeId = useRouterState({
233
241
  select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,
@@ -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,8 @@ 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
- <React.Suspense fallback={pendingElement}>
96
- <routerContext.Provider value={router}>
97
- {children}
98
- <Transitioner />
99
- </routerContext.Provider>
100
- </React.Suspense>
89
+ <routerContext.Provider value={router}>{children}</routerContext.Provider>
101
90
  )
102
91
 
103
92
  if (router.options.Wrap) {
@@ -118,129 +107,6 @@ export function RouterProvider<
118
107
  )
119
108
  }
120
109
 
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
110
  export function getRouteMatch<TRouteTree extends AnyRoute>(
245
111
  state: RouterState<TRouteTree>,
246
112
  id: string,
@@ -275,15 +141,3 @@ export type RouterProps<
275
141
  >['context']
276
142
  >
277
143
  }
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
+ }