@tanstack/react-router 1.86.1 → 1.87.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
- "version": "1.86.1",
3
+ "version": "1.87.1",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
package/src/router.ts CHANGED
@@ -1743,7 +1743,6 @@ export class Router<
1743
1743
  }
1744
1744
  maskedNext = build(maskedDest)
1745
1745
  }
1746
- // console.log('buildWithMatches', {foundMask, dest, maskedDest, maskedNext})
1747
1746
  }
1748
1747
 
1749
1748
  const nextMatches = this.getMatchedRoutes(next, dest)
@@ -2118,19 +2117,24 @@ export class Router<
2118
2117
  let updated!: AnyRouteMatch
2119
2118
  const isPending = this.state.pendingMatches?.find((d) => d.id === id)
2120
2119
  const isMatched = this.state.matches.find((d) => d.id === id)
2120
+ const isCached = this.state.cachedMatches.find((d) => d.id === id)
2121
2121
 
2122
2122
  const matchesKey = isPending
2123
2123
  ? 'pendingMatches'
2124
2124
  : isMatched
2125
2125
  ? 'matches'
2126
- : 'cachedMatches'
2126
+ : isCached
2127
+ ? 'cachedMatches'
2128
+ : ''
2127
2129
 
2128
- this.__store.setState((s) => ({
2129
- ...s,
2130
- [matchesKey]: s[matchesKey]?.map((d) =>
2131
- d.id === id ? (updated = updater(d)) : d,
2132
- ),
2133
- }))
2130
+ if (matchesKey) {
2131
+ this.__store.setState((s) => ({
2132
+ ...s,
2133
+ [matchesKey]: s[matchesKey]?.map((d) =>
2134
+ d.id === id ? (updated = updater(d)) : d,
2135
+ ),
2136
+ }))
2137
+ }
2134
2138
 
2135
2139
  return updated
2136
2140
  }
@@ -2146,7 +2150,7 @@ export class Router<
2146
2150
  loadMatches = async ({
2147
2151
  location,
2148
2152
  matches,
2149
- preload,
2153
+ preload: allPreload,
2150
2154
  onReady,
2151
2155
  updateMatch = this.updateMatch,
2152
2156
  }: {
@@ -2170,6 +2174,10 @@ export class Router<
2170
2174
  }
2171
2175
  }
2172
2176
 
2177
+ const resolvePreload = (matchId: string) => {
2178
+ return !!(allPreload && !this.state.matches.find((d) => d.id === matchId))
2179
+ }
2180
+
2173
2181
  if (!this.isServer && !this.state.matches.length) {
2174
2182
  triggerOnReady()
2175
2183
  }
@@ -2270,7 +2278,7 @@ export class Router<
2270
2278
  const shouldPending = !!(
2271
2279
  onReady &&
2272
2280
  !this.isServer &&
2273
- !preload &&
2281
+ !resolvePreload(matchId) &&
2274
2282
  (route.options.loader || route.options.beforeLoad) &&
2275
2283
  typeof pendingMs === 'number' &&
2276
2284
  pendingMs !== Infinity &&
@@ -2278,6 +2286,7 @@ export class Router<
2278
2286
  this.options.defaultPendingComponent)
2279
2287
  )
2280
2288
 
2289
+ let executeBeforeLoad = true
2281
2290
  if (
2282
2291
  // If we are in the middle of a load, either of these will be present
2283
2292
  // (not to be confused with `loadPromise`, which is always defined)
@@ -2296,8 +2305,10 @@ export class Router<
2296
2305
 
2297
2306
  // Wait for the beforeLoad to resolve before we continue
2298
2307
  await existingMatch.beforeLoadPromise
2299
- } else {
2300
- // If we are not in the middle of a load, start it
2308
+ executeBeforeLoad = this.getMatch(matchId)!.status !== 'success'
2309
+ }
2310
+ if (executeBeforeLoad) {
2311
+ // If we are not in the middle of a load OR the previous load failed, start it
2301
2312
  try {
2302
2313
  updateMatch(matchId, (prev) => ({
2303
2314
  ...prev,
@@ -2354,6 +2365,8 @@ export class Router<
2354
2365
  const { search, params, context, cause } =
2355
2366
  this.getMatch(matchId)!
2356
2367
 
2368
+ const preload = resolvePreload(matchId)
2369
+
2357
2370
  const beforeLoadFnContext: BeforeLoadContextOptions<
2358
2371
  any,
2359
2372
  any,
@@ -2364,7 +2377,7 @@ export class Router<
2364
2377
  search,
2365
2378
  abortController,
2366
2379
  params,
2367
- preload: !!preload,
2380
+ preload,
2368
2381
  context,
2369
2382
  location,
2370
2383
  navigate: (opts: any) =>
@@ -2432,7 +2445,9 @@ export class Router<
2432
2445
  (async () => {
2433
2446
  const { loaderPromise: prevLoaderPromise } =
2434
2447
  this.getMatch(matchId)!
2448
+
2435
2449
  let loaderRunningAsync = false
2450
+
2436
2451
  if (prevLoaderPromise) {
2437
2452
  await prevLoaderPromise
2438
2453
  } else {
@@ -2448,6 +2463,8 @@ export class Router<
2448
2463
  cause,
2449
2464
  } = this.getMatch(matchId)!
2450
2465
 
2466
+ const preload = resolvePreload(matchId)
2467
+
2451
2468
  return {
2452
2469
  params,
2453
2470
  deps: loaderDeps,
@@ -2466,6 +2483,8 @@ export class Router<
2466
2483
  // This is where all of the stale-while-revalidate magic happens
2467
2484
  const age = Date.now() - this.getMatch(matchId)!.updatedAt
2468
2485
 
2486
+ const preload = resolvePreload(matchId)
2487
+
2469
2488
  const staleAge = preload
2470
2489
  ? (route.options.preloadStaleTime ??
2471
2490
  this.options.defaultPreloadStaleTime ??
@@ -2686,7 +2705,7 @@ export class Router<
2686
2705
  await triggerOnReady()
2687
2706
  } catch (err) {
2688
2707
  if (isRedirect(err) || isNotFound(err)) {
2689
- if (isNotFound(err) && !preload) {
2708
+ if (isNotFound(err) && !allPreload) {
2690
2709
  await triggerOnReady()
2691
2710
  }
2692
2711
  throw err
@@ -2806,17 +2825,21 @@ export class Router<
2806
2825
  dest: opts,
2807
2826
  })
2808
2827
 
2809
- const loadedMatchIds = Object.fromEntries(
2810
- [
2811
- ...this.state.matches,
2812
- ...(this.state.pendingMatches ?? []),
2813
- ...this.state.cachedMatches,
2814
- ].map((d) => [d.id, true]),
2828
+ const activeMatchIds = new Set(
2829
+ [...this.state.matches, ...(this.state.pendingMatches ?? [])].map(
2830
+ (d) => d.id,
2831
+ ),
2815
2832
  )
2816
2833
 
2834
+ const loadedMatchIds = new Set([
2835
+ ...activeMatchIds,
2836
+ ...this.state.cachedMatches.map((d) => d.id),
2837
+ ])
2838
+
2839
+ // If the matches are already loaded, we need to add them to the cachedMatches
2817
2840
  this.__store.batch(() => {
2818
2841
  matches.forEach((match) => {
2819
- if (!loadedMatchIds[match.id]) {
2842
+ if (!loadedMatchIds.has(match.id)) {
2820
2843
  this.__store.setState((s) => ({
2821
2844
  ...s,
2822
2845
  cachedMatches: [...(s.cachedMatches as any), match],
@@ -2825,18 +2848,13 @@ export class Router<
2825
2848
  })
2826
2849
  })
2827
2850
 
2828
- const activeMatchIds = new Set(
2829
- [...this.state.matches, ...(this.state.pendingMatches ?? [])].map(
2830
- (d) => d.id,
2831
- ),
2832
- )
2833
-
2834
2851
  try {
2835
2852
  matches = await this.loadMatches({
2836
2853
  matches,
2837
2854
  location: next,
2838
2855
  preload: true,
2839
2856
  updateMatch: (id, updater) => {
2857
+ // Don't update the match if it's currently loaded
2840
2858
  if (activeMatchIds.has(id)) {
2841
2859
  matches = matches.map((d) => (d.id === id ? updater(d) : d))
2842
2860
  } else {