@tanstack/react-router 0.0.1-beta.234 → 0.0.1-beta.236
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/build/cjs/Matches.js +9 -8
- package/build/cjs/Matches.js.map +1 -1
- package/build/cjs/RouterProvider.js +28 -31
- package/build/cjs/RouterProvider.js.map +1 -1
- package/build/cjs/router.js +49 -37
- package/build/cjs/router.js.map +1 -1
- package/build/esm/index.js +86 -76
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +566 -285
- package/build/types/router.d.ts +6 -3
- package/build/umd/index.development.js +354 -80
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +2 -2
- package/build/umd/index.production.js.map +1 -1
- package/package.json +4 -2
- package/src/Matches.tsx +23 -10
- package/src/RouterProvider.tsx +35 -34
- package/src/router.ts +62 -44
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-router",
|
|
3
3
|
"author": "Tanner Linsley",
|
|
4
|
-
"version": "0.0.1-beta.
|
|
4
|
+
"version": "0.0.1-beta.236",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "tanstack/router",
|
|
7
7
|
"homepage": "https://tanstack.com/router",
|
|
@@ -40,9 +40,11 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@babel/runtime": "^7.16.7",
|
|
43
|
+
"@tanstack/react-store": "^0.2.1",
|
|
44
|
+
"@tanstack/store": "^0.1.3",
|
|
43
45
|
"tiny-invariant": "^1.3.1",
|
|
44
46
|
"tiny-warning": "^1.0.3",
|
|
45
|
-
"@tanstack/history": "0.0.1-beta.
|
|
47
|
+
"@tanstack/history": "0.0.1-beta.236"
|
|
46
48
|
},
|
|
47
49
|
"scripts": {
|
|
48
50
|
"build": "rollup --config rollup.config.js"
|
package/src/Matches.tsx
CHANGED
|
@@ -47,11 +47,12 @@ export interface RouteMatch<
|
|
|
47
47
|
export type AnyRouteMatch = RouteMatch<any>
|
|
48
48
|
|
|
49
49
|
export function Matches() {
|
|
50
|
-
const { routesById
|
|
51
|
-
const
|
|
52
|
-
|
|
50
|
+
const { routesById } = useRouter()
|
|
51
|
+
const routerState = useRouterState()
|
|
52
|
+
const matches = routerState.pendingMatches?.some((d) => d.showPending)
|
|
53
|
+
? routerState.pendingMatches
|
|
54
|
+
: routerState.matches
|
|
53
55
|
const locationKey = useRouterState().location.state.key
|
|
54
|
-
|
|
55
56
|
const route = routesById[rootRouteId]!
|
|
56
57
|
|
|
57
58
|
const errorComponent = React.useCallback(
|
|
@@ -307,9 +308,13 @@ export function useMatch<
|
|
|
307
308
|
|
|
308
309
|
const matchRouteId = useRouterState({
|
|
309
310
|
select: (state) => {
|
|
311
|
+
const matches = state.pendingMatches?.some((d) => d.showPending)
|
|
312
|
+
? state.pendingMatches
|
|
313
|
+
: state.matches
|
|
314
|
+
|
|
310
315
|
const match = opts?.from
|
|
311
|
-
?
|
|
312
|
-
:
|
|
316
|
+
? matches.find((d) => d.routeId === opts?.from)
|
|
317
|
+
: matches.find((d) => d.id === nearestMatch.id)
|
|
313
318
|
|
|
314
319
|
return match!.routeId
|
|
315
320
|
},
|
|
@@ -330,9 +335,13 @@ export function useMatch<
|
|
|
330
335
|
|
|
331
336
|
const matchSelection = useRouterState({
|
|
332
337
|
select: (state) => {
|
|
338
|
+
const matches = state.pendingMatches?.some((d) => d.showPending)
|
|
339
|
+
? state.pendingMatches
|
|
340
|
+
: state.matches
|
|
341
|
+
|
|
333
342
|
const match = opts?.from
|
|
334
|
-
?
|
|
335
|
-
:
|
|
343
|
+
? matches.find((d) => d.routeId === opts?.from)
|
|
344
|
+
: matches.find((d) => d.id === nearestMatch.id)
|
|
336
345
|
|
|
337
346
|
invariant(
|
|
338
347
|
match,
|
|
@@ -359,8 +368,12 @@ export function useMatches<T = RouteMatch[]>(opts?: {
|
|
|
359
368
|
|
|
360
369
|
return useRouterState({
|
|
361
370
|
select: (state) => {
|
|
362
|
-
|
|
363
|
-
state.
|
|
371
|
+
let matches = state.pendingMatches?.some((d) => d.showPending)
|
|
372
|
+
? state.pendingMatches
|
|
373
|
+
: state.matches
|
|
374
|
+
|
|
375
|
+
matches = matches.slice(
|
|
376
|
+
matches.findIndex((d) => d.id === contextMatches[0]?.id),
|
|
364
377
|
)
|
|
365
378
|
return opts?.select ? opts.select(matches) : (matches as T)
|
|
366
379
|
},
|
package/src/RouterProvider.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import warning from 'tiny-warning'
|
|
3
|
+
import { useStore } from '@tanstack/react-store'
|
|
3
4
|
import { Matches } from './Matches'
|
|
4
5
|
import {
|
|
5
6
|
LinkInfo,
|
|
@@ -99,39 +100,33 @@ function RouterProviderInner<
|
|
|
99
100
|
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
100
101
|
TDehydrated extends Record<string, any> = Record<string, any>,
|
|
101
102
|
>({ router }: RouterProps<TRouteTree, TDehydrated>) {
|
|
102
|
-
const [preState, setState] = React.useState(() => router.state)
|
|
103
103
|
const [isTransitioning, startReactTransition] = React.useTransition()
|
|
104
|
-
const isAnyTransitioning =
|
|
105
|
-
isTransitioning || preState.matches.some((d) => d.status === 'pending')
|
|
106
|
-
|
|
107
|
-
const state = React.useMemo<RouterState<TRouteTree>>(
|
|
108
|
-
() => ({
|
|
109
|
-
...preState,
|
|
110
|
-
status: isAnyTransitioning ? 'pending' : 'idle',
|
|
111
|
-
location: isTransitioning ? router.latestLocation : preState.location,
|
|
112
|
-
pendingMatches: router.pendingMatches,
|
|
113
|
-
}),
|
|
114
|
-
[preState, isTransitioning],
|
|
115
|
-
)
|
|
116
104
|
|
|
117
|
-
router.setState = setState
|
|
118
|
-
router.state = state
|
|
119
105
|
router.startReactTransition = startReactTransition
|
|
120
106
|
|
|
107
|
+
React.useEffect(() => {
|
|
108
|
+
if (isTransitioning) {
|
|
109
|
+
router.__store.setState((s) => ({
|
|
110
|
+
...s,
|
|
111
|
+
isTransitioning,
|
|
112
|
+
}))
|
|
113
|
+
}
|
|
114
|
+
}, [isTransitioning])
|
|
115
|
+
|
|
121
116
|
const tryLoad = () => {
|
|
122
|
-
startReactTransition(() => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
})
|
|
117
|
+
// startReactTransition(() => {
|
|
118
|
+
try {
|
|
119
|
+
router.load()
|
|
120
|
+
} catch (err) {
|
|
121
|
+
console.error(err)
|
|
122
|
+
}
|
|
123
|
+
// })
|
|
129
124
|
}
|
|
130
125
|
|
|
131
126
|
useLayoutEffect(() => {
|
|
132
127
|
const unsub = router.history.subscribe(() => {
|
|
133
128
|
router.latestLocation = router.parseLocation(router.latestLocation)
|
|
134
|
-
if (state.location !== router.latestLocation) {
|
|
129
|
+
if (router.state.location !== router.latestLocation) {
|
|
135
130
|
tryLoad()
|
|
136
131
|
}
|
|
137
132
|
})
|
|
@@ -143,7 +138,7 @@ function RouterProviderInner<
|
|
|
143
138
|
state: true,
|
|
144
139
|
})
|
|
145
140
|
|
|
146
|
-
if (state.location.href !== nextLocation.href) {
|
|
141
|
+
if (router.state.location.href !== nextLocation.href) {
|
|
147
142
|
router.commitLocation({ ...nextLocation, replace: true })
|
|
148
143
|
}
|
|
149
144
|
|
|
@@ -153,21 +148,26 @@ function RouterProviderInner<
|
|
|
153
148
|
}, [router.history])
|
|
154
149
|
|
|
155
150
|
useLayoutEffect(() => {
|
|
156
|
-
if (
|
|
151
|
+
if (
|
|
152
|
+
!isTransitioning &&
|
|
153
|
+
router.state.resolvedLocation !== router.state.location
|
|
154
|
+
) {
|
|
157
155
|
router.emit({
|
|
158
156
|
type: 'onResolved',
|
|
159
|
-
fromLocation: state.resolvedLocation,
|
|
160
|
-
toLocation: state.location,
|
|
161
|
-
pathChanged:
|
|
157
|
+
fromLocation: router.state.resolvedLocation,
|
|
158
|
+
toLocation: router.state.location,
|
|
159
|
+
pathChanged:
|
|
160
|
+
router.state.location!.href !== router.state.resolvedLocation?.href,
|
|
162
161
|
})
|
|
163
162
|
router.pendingMatches = []
|
|
164
163
|
|
|
165
|
-
setState((s) => ({
|
|
164
|
+
router.__store.setState((s) => ({
|
|
166
165
|
...s,
|
|
166
|
+
isTransitioning: false,
|
|
167
167
|
resolvedLocation: s.location,
|
|
168
168
|
}))
|
|
169
169
|
}
|
|
170
|
-
})
|
|
170
|
+
}, [isTransitioning])
|
|
171
171
|
|
|
172
172
|
useLayoutEffect(() => {
|
|
173
173
|
if (!window.__TSR_DEHYDRATED__) {
|
|
@@ -186,7 +186,9 @@ export function getRouteMatch<TRouteTree extends AnyRoute>(
|
|
|
186
186
|
state: RouterState<TRouteTree>,
|
|
187
187
|
id: string,
|
|
188
188
|
): undefined | RouteMatch<TRouteTree> {
|
|
189
|
-
return [...state.pendingMatches, ...state.matches].find(
|
|
189
|
+
return [...(state.pendingMatches ?? []), ...state.matches].find(
|
|
190
|
+
(d) => d.id === id,
|
|
191
|
+
)
|
|
190
192
|
}
|
|
191
193
|
|
|
192
194
|
export function useRouterState<
|
|
@@ -194,9 +196,8 @@ export function useRouterState<
|
|
|
194
196
|
>(opts?: {
|
|
195
197
|
select: (state: RouterState<RegisteredRouter['routeTree']>) => TSelected
|
|
196
198
|
}): TSelected {
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
return opts?.select ? opts.select(state) : (state as any)
|
|
199
|
+
const router = useRouter()
|
|
200
|
+
return useStore(router.__store, opts?.select as any)
|
|
200
201
|
}
|
|
201
202
|
|
|
202
203
|
export type RouterProps<
|
package/src/router.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
createBrowserHistory,
|
|
6
6
|
createMemoryHistory,
|
|
7
7
|
} from '@tanstack/history'
|
|
8
|
+
import { Store } from '@tanstack/store'
|
|
8
9
|
|
|
9
10
|
//
|
|
10
11
|
|
|
@@ -29,7 +30,6 @@ import {
|
|
|
29
30
|
functionalUpdate,
|
|
30
31
|
last,
|
|
31
32
|
pick,
|
|
32
|
-
PickAsPartial,
|
|
33
33
|
} from './utils'
|
|
34
34
|
import {
|
|
35
35
|
ErrorRouteComponent,
|
|
@@ -134,8 +134,10 @@ export interface RouterOptions<
|
|
|
134
134
|
|
|
135
135
|
export interface RouterState<TRouteTree extends AnyRoute = AnyRoute> {
|
|
136
136
|
status: 'pending' | 'idle'
|
|
137
|
+
isLoading: boolean
|
|
138
|
+
isTransitioning: boolean
|
|
137
139
|
matches: RouteMatch<TRouteTree>[]
|
|
138
|
-
pendingMatches
|
|
140
|
+
pendingMatches?: RouteMatch<TRouteTree>[]
|
|
139
141
|
location: ParsedLocation<FullSearchSchema<TRouteTree>>
|
|
140
142
|
resolvedLocation: ParsedLocation<FullSearchSchema<TRouteTree>>
|
|
141
143
|
lastUpdated: number
|
|
@@ -236,7 +238,7 @@ export class Router<
|
|
|
236
238
|
dehydratedData?: TDehydrated
|
|
237
239
|
|
|
238
240
|
// Must build in constructor
|
|
239
|
-
|
|
241
|
+
__store!: Store<RouterState<TRouteTree>>
|
|
240
242
|
options!: PickAsRequired<
|
|
241
243
|
RouterOptions<TRouteTree, TDehydrated>,
|
|
242
244
|
'stringifySearch' | 'parseSearch' | 'context'
|
|
@@ -265,11 +267,6 @@ export class Router<
|
|
|
265
267
|
// by the router provider once rendered. We provide these so that the
|
|
266
268
|
// router can be used in a non-react environment if necessary
|
|
267
269
|
startReactTransition: (fn: () => void) => void = (fn) => fn()
|
|
268
|
-
setState: (updater: NonNullableUpdater<RouterState<TRouteTree>>) => void = (
|
|
269
|
-
updater,
|
|
270
|
-
) => {
|
|
271
|
-
this.state = functionalUpdate(updater, this.state)
|
|
272
|
-
}
|
|
273
270
|
|
|
274
271
|
update = (newOptions: RouterConstructorOptions<TRouteTree, TDehydrated>) => {
|
|
275
272
|
this.options = {
|
|
@@ -296,11 +293,25 @@ export class Router<
|
|
|
296
293
|
this.buildRouteTree()
|
|
297
294
|
}
|
|
298
295
|
|
|
299
|
-
if (!this.
|
|
300
|
-
this.
|
|
296
|
+
if (!this.__store) {
|
|
297
|
+
this.__store = new Store(getInitialRouterState(this.latestLocation), {
|
|
298
|
+
onUpdate: () => {
|
|
299
|
+
this.__store.state = {
|
|
300
|
+
...this.state,
|
|
301
|
+
status:
|
|
302
|
+
this.state.isTransitioning || this.state.isLoading
|
|
303
|
+
? 'pending'
|
|
304
|
+
: 'idle',
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
})
|
|
301
308
|
}
|
|
302
309
|
}
|
|
303
310
|
|
|
311
|
+
get state() {
|
|
312
|
+
return this.__store.state
|
|
313
|
+
}
|
|
314
|
+
|
|
304
315
|
buildRouteTree = () => {
|
|
305
316
|
this.routesById = {} as RoutesById<TRouteTree>
|
|
306
317
|
this.routesByPath = {} as RoutesByPath<TRouteTree>
|
|
@@ -654,7 +665,7 @@ export class Router<
|
|
|
654
665
|
}
|
|
655
666
|
|
|
656
667
|
cancelMatches = () => {
|
|
657
|
-
this.state.
|
|
668
|
+
this.state.pendingMatches?.forEach((match) => {
|
|
658
669
|
this.cancelMatch(match.id)
|
|
659
670
|
})
|
|
660
671
|
}
|
|
@@ -948,6 +959,15 @@ export class Router<
|
|
|
948
959
|
let latestPromise
|
|
949
960
|
let firstBadMatchIndex: number | undefined
|
|
950
961
|
|
|
962
|
+
const updatePendingMatch = (match: AnyRouteMatch) => {
|
|
963
|
+
this.__store.setState((s) => ({
|
|
964
|
+
...s,
|
|
965
|
+
pendingMatches: s.pendingMatches?.map((d) =>
|
|
966
|
+
d.id === match.id ? match : d,
|
|
967
|
+
),
|
|
968
|
+
}))
|
|
969
|
+
}
|
|
970
|
+
|
|
951
971
|
// Check each match middleware to see if the route can be accessed
|
|
952
972
|
try {
|
|
953
973
|
for (let [index, match] of matches.entries()) {
|
|
@@ -1149,13 +1169,12 @@ export class Router<
|
|
|
1149
1169
|
}
|
|
1150
1170
|
|
|
1151
1171
|
if (!preload) {
|
|
1152
|
-
|
|
1153
|
-
...s,
|
|
1154
|
-
matches: s.matches.map((d) => (d.id === match.id ? match : d)),
|
|
1155
|
-
}))
|
|
1172
|
+
updatePendingMatch(match)
|
|
1156
1173
|
}
|
|
1157
1174
|
|
|
1158
1175
|
let didShowPending = false
|
|
1176
|
+
const pendingMinMs =
|
|
1177
|
+
route.options.pendingMinMs ?? this.options.defaultPendingMinMs
|
|
1159
1178
|
|
|
1160
1179
|
await new Promise<void>(async (resolve) => {
|
|
1161
1180
|
// If the route has a pending component and a pendingMs option,
|
|
@@ -1170,12 +1189,7 @@ export class Router<
|
|
|
1170
1189
|
showPending: true,
|
|
1171
1190
|
}
|
|
1172
1191
|
|
|
1173
|
-
|
|
1174
|
-
...s,
|
|
1175
|
-
matches: s.matches.map((d) =>
|
|
1176
|
-
d.id === match.id ? match : d,
|
|
1177
|
-
),
|
|
1178
|
-
}))
|
|
1192
|
+
updatePendingMatch(match)
|
|
1179
1193
|
resolve()
|
|
1180
1194
|
})
|
|
1181
1195
|
}
|
|
@@ -1184,9 +1198,6 @@ export class Router<
|
|
|
1184
1198
|
const loaderData = await loadPromise
|
|
1185
1199
|
if ((latestPromise = checkLatest())) return await latestPromise
|
|
1186
1200
|
|
|
1187
|
-
const pendingMinMs =
|
|
1188
|
-
route.options.pendingMinMs ?? this.options.defaultPendingMinMs
|
|
1189
|
-
|
|
1190
1201
|
if (didShowPending && pendingMinMs) {
|
|
1191
1202
|
await new Promise((r) => setTimeout(r, pendingMinMs))
|
|
1192
1203
|
}
|
|
@@ -1220,13 +1231,22 @@ export class Router<
|
|
|
1220
1231
|
isFetching: false,
|
|
1221
1232
|
updatedAt: Date.now(),
|
|
1222
1233
|
}
|
|
1234
|
+
} finally {
|
|
1235
|
+
// If we showed the pending component, that means
|
|
1236
|
+
// we already moved the pendingMatches to the matches
|
|
1237
|
+
// state, so we need to update that specific match
|
|
1238
|
+
if (didShowPending && pendingMinMs && match.showPending) {
|
|
1239
|
+
this.__store.setState((s) => ({
|
|
1240
|
+
...s,
|
|
1241
|
+
matches: s.matches?.map((d) =>
|
|
1242
|
+
d.id === match.id ? match : d,
|
|
1243
|
+
),
|
|
1244
|
+
}))
|
|
1245
|
+
}
|
|
1223
1246
|
}
|
|
1224
1247
|
|
|
1225
1248
|
if (!preload) {
|
|
1226
|
-
|
|
1227
|
-
...s,
|
|
1228
|
-
matches: s.matches.map((d) => (d.id === match.id ? match : d)),
|
|
1229
|
-
}))
|
|
1249
|
+
updatePendingMatch(match)
|
|
1230
1250
|
}
|
|
1231
1251
|
|
|
1232
1252
|
resolve()
|
|
@@ -1262,7 +1282,7 @@ export class Router<
|
|
|
1262
1282
|
})
|
|
1263
1283
|
|
|
1264
1284
|
// Match the routes
|
|
1265
|
-
let
|
|
1285
|
+
let pendingMatches: RouteMatch<any, any>[] = this.matchRoutes(
|
|
1266
1286
|
next.pathname,
|
|
1267
1287
|
next.search,
|
|
1268
1288
|
{
|
|
@@ -1270,23 +1290,21 @@ export class Router<
|
|
|
1270
1290
|
},
|
|
1271
1291
|
)
|
|
1272
1292
|
|
|
1273
|
-
this.pendingMatches = matches
|
|
1274
|
-
|
|
1275
1293
|
const previousMatches = this.state.matches
|
|
1276
1294
|
|
|
1277
1295
|
// Ingest the new matches
|
|
1278
|
-
this.setState((s) => ({
|
|
1296
|
+
this.__store.setState((s) => ({
|
|
1279
1297
|
...s,
|
|
1280
|
-
|
|
1298
|
+
isLoading: true,
|
|
1281
1299
|
location: next,
|
|
1282
|
-
|
|
1300
|
+
pendingMatches,
|
|
1283
1301
|
}))
|
|
1284
1302
|
|
|
1285
1303
|
try {
|
|
1286
1304
|
try {
|
|
1287
1305
|
// Load the matches
|
|
1288
1306
|
await this.loadMatches({
|
|
1289
|
-
matches,
|
|
1307
|
+
matches: pendingMatches,
|
|
1290
1308
|
checkLatest: () => this.checkLatest(promise),
|
|
1291
1309
|
invalidate: opts?.invalidate,
|
|
1292
1310
|
})
|
|
@@ -1310,12 +1328,12 @@ export class Router<
|
|
|
1310
1328
|
this.pendingMatches.includes(id),
|
|
1311
1329
|
)
|
|
1312
1330
|
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1331
|
+
this.__store.setState((s) => ({
|
|
1332
|
+
...s,
|
|
1333
|
+
isLoading: false,
|
|
1334
|
+
matches: pendingMatches,
|
|
1335
|
+
pendingMatches: undefined,
|
|
1336
|
+
}))
|
|
1319
1337
|
|
|
1320
1338
|
//
|
|
1321
1339
|
;(
|
|
@@ -1517,8 +1535,6 @@ export class Router<
|
|
|
1517
1535
|
? this.latestLocation
|
|
1518
1536
|
: this.state.resolvedLocation
|
|
1519
1537
|
|
|
1520
|
-
// const baseLocation = state.resolvedLocation
|
|
1521
|
-
|
|
1522
1538
|
if (!baseLocation) {
|
|
1523
1539
|
return false
|
|
1524
1540
|
}
|
|
@@ -1633,7 +1649,7 @@ export class Router<
|
|
|
1633
1649
|
return match
|
|
1634
1650
|
})
|
|
1635
1651
|
|
|
1636
|
-
this.setState((s) => {
|
|
1652
|
+
this.__store.setState((s) => {
|
|
1637
1653
|
return {
|
|
1638
1654
|
...s,
|
|
1639
1655
|
matches: matches as any,
|
|
@@ -1672,6 +1688,8 @@ export function getInitialRouterState(
|
|
|
1672
1688
|
location: ParsedLocation,
|
|
1673
1689
|
): RouterState<any> {
|
|
1674
1690
|
return {
|
|
1691
|
+
isLoading: false,
|
|
1692
|
+
isTransitioning: false,
|
|
1675
1693
|
status: 'idle',
|
|
1676
1694
|
resolvedLocation: location,
|
|
1677
1695
|
location,
|