@tanstack/router-core 1.166.2 → 1.166.4

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/router-core",
3
- "version": "1.166.2",
3
+ "version": "1.166.4",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -33,6 +33,7 @@ type InnerLoadContext = {
33
33
  updateMatch: UpdateMatchFn
34
34
  matches: Array<AnyRouteMatch>
35
35
  preload?: boolean
36
+ forceStaleReload?: boolean
36
37
  onReady?: () => Promise<void>
37
38
  sync?: boolean
38
39
  }
@@ -166,7 +167,10 @@ const shouldSkipLoader = (
166
167
  inner: InnerLoadContext,
167
168
  matchId: string,
168
169
  ): boolean => {
169
- const match = inner.router.getMatch(matchId)!
170
+ const match = inner.router.getMatch(matchId)
171
+ if (!match) {
172
+ return true
173
+ }
170
174
  // upon hydration, we skip the loader if the match has been dehydrated on the server
171
175
  if (!(isServer ?? inner.router.isServer) && match._nonReactive.dehydrated) {
172
176
  return true
@@ -179,6 +183,21 @@ const shouldSkipLoader = (
179
183
  return false
180
184
  }
181
185
 
186
+ const syncMatchContext = (
187
+ inner: InnerLoadContext,
188
+ matchId: string,
189
+ index: number,
190
+ ): void => {
191
+ const nextContext = buildMatchContext(inner, index)
192
+
193
+ inner.updateMatch(matchId, (prev) => {
194
+ return {
195
+ ...prev,
196
+ context: nextContext,
197
+ }
198
+ })
199
+ }
200
+
182
201
  const handleSerialError = (
183
202
  inner: InnerLoadContext,
184
203
  index: number,
@@ -479,8 +498,6 @@ const executeBeforeLoad = (
479
498
 
480
499
  batch(() => {
481
500
  pending()
482
- // Only store __beforeLoadContext here, don't update context yet
483
- // Context will be updated in loadRouteMatch after loader completes
484
501
  inner.updateMatch(matchId, (prev) => ({
485
502
  ...prev,
486
503
  __beforeLoadContext: beforeLoadContext,
@@ -762,6 +779,7 @@ const loadRouteMatch = async (
762
779
  async function handleLoader(
763
780
  preload: boolean,
764
781
  prevMatch: AnyRouteMatch,
782
+ previousRouteMatchId: string | undefined,
765
783
  match: AnyRouteMatch,
766
784
  route: AnyRoute,
767
785
  ) {
@@ -787,8 +805,15 @@ const loadRouteMatch = async (
787
805
 
788
806
  // If the route is successful and still fresh, just resolve
789
807
  const { status, invalid } = match
808
+ const staleMatchShouldReload =
809
+ age > staleAge &&
810
+ (!!inner.forceStaleReload ||
811
+ match.cause === 'enter' ||
812
+ (previousRouteMatchId !== undefined &&
813
+ previousRouteMatchId !== match.id))
790
814
  loaderShouldRunAsync =
791
- status === 'success' && (invalid || (shouldReload ?? age > staleAge))
815
+ status === 'success' &&
816
+ (invalid || (shouldReload ?? staleMatchShouldReload))
792
817
  if (preload && route.options.preload === false) {
793
818
  // Do nothing
794
819
  } else if (loaderShouldRunAsync && !inner.sync) {
@@ -808,6 +833,8 @@ const loadRouteMatch = async (
808
833
  })()
809
834
  } else if (status !== 'success' || (loaderShouldRunAsync && inner.sync)) {
810
835
  await runLoader(inner, matchPromises, matchId, index, route)
836
+ } else {
837
+ syncMatchContext(inner, matchId, index)
811
838
  }
812
839
  }
813
840
 
@@ -817,11 +844,22 @@ const loadRouteMatch = async (
817
844
  const route = inner.router.looseRoutesById[routeId]!
818
845
 
819
846
  if (shouldSkipLoader(inner, matchId)) {
847
+ const match = inner.router.getMatch(matchId)
848
+ if (!match) {
849
+ return inner.matches[index]!
850
+ }
851
+
852
+ syncMatchContext(inner, matchId, index)
853
+
820
854
  if (isServer ?? inner.router.isServer) {
821
855
  return inner.router.getMatch(matchId)!
822
856
  }
823
857
  } else {
824
858
  const prevMatch = inner.router.getMatch(matchId)! // This is where all of the stale-while-revalidate magic happens
859
+ const previousRouteMatchId =
860
+ inner.router.state.matches[index]?.routeId === routeId
861
+ ? inner.router.state.matches[index]!.id
862
+ : inner.router.state.matches.find((d) => d.routeId === routeId)?.id
825
863
  const preload = resolvePreload(inner, matchId)
826
864
 
827
865
  // there is a loaderPromise, so we are in the middle of a load
@@ -840,7 +878,13 @@ const loadRouteMatch = async (
840
878
  }
841
879
 
842
880
  if (match.status === 'pending') {
843
- await handleLoader(preload, prevMatch, match, route)
881
+ await handleLoader(
882
+ preload,
883
+ prevMatch,
884
+ previousRouteMatchId,
885
+ match,
886
+ route,
887
+ )
844
888
  }
845
889
  } else {
846
890
  const nextPreload =
@@ -854,7 +898,7 @@ const loadRouteMatch = async (
854
898
  }))
855
899
  }
856
900
 
857
- await handleLoader(preload, prevMatch, match, route)
901
+ await handleLoader(preload, prevMatch, previousRouteMatchId, match, route)
858
902
  }
859
903
  }
860
904
  const match = inner.router.getMatch(matchId)!
@@ -886,6 +930,7 @@ export async function loadMatches(arg: {
886
930
  location: ParsedLocation
887
931
  matches: Array<AnyRouteMatch>
888
932
  preload?: boolean
933
+ forceStaleReload?: boolean
889
934
  onReady?: () => Promise<void>
890
935
  updateMatch: UpdateMatchFn
891
936
  sync?: boolean
package/src/router.ts CHANGED
@@ -2364,6 +2364,7 @@ export class RouterCore<
2364
2364
  let redirect: AnyRedirect | undefined
2365
2365
  let notFound: NotFoundError | undefined
2366
2366
  let loadPromise: Promise<void>
2367
+ const previousLocation = this.state.resolvedLocation ?? this.state.location
2367
2368
 
2368
2369
  // eslint-disable-next-line prefer-const
2369
2370
  loadPromise = new Promise<void>((resolve) => {
@@ -2394,6 +2395,7 @@ export class RouterCore<
2394
2395
  await loadMatches({
2395
2396
  router: this,
2396
2397
  sync: opts?.sync,
2398
+ forceStaleReload: previousLocation.href === next.href,
2397
2399
  matches: this.state.pendingMatches as Array<AnyRouteMatch>,
2398
2400
  location: next,
2399
2401
  updateMatch: this.updateMatch,