@tanstack/react-router 0.0.1-beta.279 → 0.0.1-beta.280

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.
@@ -584,7 +584,6 @@ function Transitioner() {
584
584
  }
585
585
  }
586
586
  }
587
- router.pendingMatches = [];
588
587
  router.__store.setState(s => ({
589
588
  ...s,
590
589
  isTransitioning: false,
@@ -600,7 +599,7 @@ function Transitioner() {
600
599
  return null;
601
600
  }
602
601
  function getRouteMatch(state, id) {
603
- return [...state.preloadMatches, ...(state.pendingMatches ?? []), ...state.matches].find(d => d.id === id);
602
+ return [...state.cachedMatches, ...(state.pendingMatches ?? []), ...state.matches].find(d => d.id === id);
604
603
  }
605
604
  function useRouterState(opts) {
606
605
  const router = useRouter();
@@ -1147,7 +1146,7 @@ function useLinkProps(options) {
1147
1146
  // Combine the matches based on user router.options
1148
1147
  const pathTest = activeOptions?.exact ? s.location.pathname === next.pathname : pathIsFuzzyEqual;
1149
1148
  const hashTest = activeOptions?.includeHash ? s.location.hash === next.hash : true;
1150
- const searchTest = activeOptions?.includeSearch ?? true ? deepEqual(s.location.search, next.search, true) : true;
1149
+ const searchTest = activeOptions?.includeSearch ?? true ? deepEqual(s.location.search, next.search, !activeOptions?.exact) : true;
1151
1150
 
1152
1151
  // The final "active" test
1153
1152
  return pathTest && hashTest && searchTest;
@@ -1394,7 +1393,6 @@ class Router {
1394
1393
  navigateTimeout = null;
1395
1394
  latestLoadPromise = Promise.resolve();
1396
1395
  subscribers = new Set();
1397
- pendingMatches = [];
1398
1396
  injectedHtml = [];
1399
1397
 
1400
1398
  // Must build in constructor
@@ -1718,7 +1716,6 @@ class Router {
1718
1716
  routeContext: undefined,
1719
1717
  context: undefined,
1720
1718
  abortController: new AbortController(),
1721
- shouldReloadDeps: undefined,
1722
1719
  fetchCount: 0,
1723
1720
  cause,
1724
1721
  loaderDeps
@@ -1930,9 +1927,10 @@ class Router {
1930
1927
  let latestPromise;
1931
1928
  let firstBadMatchIndex;
1932
1929
  const updateMatch = match => {
1933
- // const isPreload = this.state.preloadMatches.find((d) => d.id === match.id)
1930
+ // const isPreload = this.state.cachedMatches.find((d) => d.id === match.id)
1934
1931
  const isPending = this.state.pendingMatches?.find(d => d.id === match.id);
1935
- const matchesKey = preload ? 'preloadMatches' : isPending ? 'pendingMatches' : 'matches';
1932
+ const isMatched = this.state.matches.find(d => d.id === match.id);
1933
+ const matchesKey = isPending ? 'pendingMatches' : isMatched ? 'matches' : 'cachedMatches';
1936
1934
  this.__store.setState(s => ({
1937
1935
  ...s,
1938
1936
  [matchesKey]: s[matchesKey]?.map(d => d.id === match.id ? match : d)
@@ -2057,65 +2055,24 @@ class Router {
2057
2055
  }),
2058
2056
  cause: preload ? 'preload' : match.cause
2059
2057
  };
2060
-
2061
- // Default to reloading the route all the time
2062
- let shouldLoad = true;
2063
- const shouldReloadFn = route.options.shouldReload;
2064
- let shouldReloadDeps = typeof shouldReloadFn === 'function' ? shouldReloadFn(loaderContext) : !!(shouldReloadFn ?? true);
2065
- const compareDeps = () => {
2066
- if (typeof shouldReloadDeps === 'object') {
2067
- // compare the deps to see if they've changed
2068
- shouldLoad = !deepEqual(shouldReloadDeps, match.shouldReloadDeps);
2069
- } else {
2070
- shouldLoad = !!shouldReloadDeps;
2071
- }
2072
- };
2073
-
2074
- // If it's the first preload, or the route is entering, or we're
2075
- // invalidating, we definitely need to load the route
2076
- if (invalidate) ; else if (preload) {
2077
- if (!match.fetchCount) ; else {
2078
- compareDeps();
2079
- }
2080
- } else if (match.cause === 'enter') {
2081
- if (!match.fetchCount) ; else {
2082
- compareDeps();
2083
- }
2084
- } else {
2085
- compareDeps();
2086
- }
2087
- if (typeof shouldReloadDeps === 'object') {
2088
- matches[index] = match = {
2089
- ...match,
2090
- shouldReloadDeps
2091
- };
2058
+ if (match.fetchCount && match.status === 'success') {
2059
+ resolve();
2092
2060
  }
2093
2061
 
2094
- // If the user doesn't want the route to reload, just
2095
- // resolve with the existing loader data
2096
-
2097
- if (!shouldLoad) {
2098
- loadPromise = Promise.resolve(match.loaderData);
2099
- } else {
2100
- if (match.fetchCount && match.status === 'success') {
2101
- resolve();
2062
+ // Otherwise, load the route
2063
+ matches[index] = match = {
2064
+ ...match,
2065
+ isFetching: true,
2066
+ fetchCount: match.fetchCount + 1
2067
+ };
2068
+ const componentsPromise = Promise.all(componentTypes.map(async type => {
2069
+ const component = route.options[type];
2070
+ if (component?.preload) {
2071
+ await component.preload();
2102
2072
  }
2103
-
2104
- // Otherwise, load the route
2105
- matches[index] = match = {
2106
- ...match,
2107
- isFetching: true,
2108
- fetchCount: match.fetchCount + 1
2109
- };
2110
- const componentsPromise = Promise.all(componentTypes.map(async type => {
2111
- const component = route.options[type];
2112
- if (component?.preload) {
2113
- await component.preload();
2114
- }
2115
- }));
2116
- const loaderPromise = route.options.loader?.(loaderContext);
2117
- loadPromise = Promise.all([componentsPromise, loaderPromise]).then(d => d[1]);
2118
- }
2073
+ }));
2074
+ const loaderPromise = route.options.loader?.(loaderContext);
2075
+ loadPromise = Promise.all([componentsPromise, loaderPromise]).then(d => d[1]);
2119
2076
  }
2120
2077
  matches[index] = match = {
2121
2078
  ...match,
@@ -2154,24 +2111,23 @@ class Router {
2154
2111
  ...match,
2155
2112
  error,
2156
2113
  status: 'error',
2157
- isFetching: false,
2158
- updatedAt: Date.now()
2114
+ isFetching: false
2159
2115
  };
2160
- } finally {
2161
- // If we showed the pending component, that means
2162
- // we already moved the pendingMatches to the matches
2163
- // state, so we need to update that specific match
2164
- if (didShowPending && pendingMinMs && match.showPending) {
2165
- updateMatch(match);
2166
- }
2167
2116
  }
2168
2117
  updateMatch(match);
2169
2118
  };
2170
- if (match.fetchCount && match.status === 'success') {
2171
- // Background Fetching
2172
- fetch();
2119
+
2120
+ // This is where all of the stale-while-revalidate magic happens
2121
+ const age = Date.now() - match.updatedAt;
2122
+ let staleAge = preload ? route.options.preloadStaleTime ?? this.options.defaultPreloadStaleTime ?? 30_000 // 30 seconds for preloads by default
2123
+ : route.options.staleTime ?? this.options.defaultStaleTime ?? 0;
2124
+ if (match.status === 'success') {
2125
+ // Background Fetching, no need to wait
2126
+ if (age > staleAge) {
2127
+ fetch();
2128
+ }
2173
2129
  } else {
2174
- // Critical Fetching
2130
+ // Critical Fetching, we need to await
2175
2131
 
2176
2132
  // If we need to potentially show the pending component,
2177
2133
  // start a timer to show it after the pendingMs
@@ -2190,9 +2146,6 @@ class Router {
2190
2146
  await fetch();
2191
2147
  }
2192
2148
  resolve();
2193
- // No Fetching
2194
-
2195
- resolve();
2196
2149
  }));
2197
2150
  });
2198
2151
  await Promise.all(matchPromises);
@@ -2219,12 +2172,16 @@ class Router {
2219
2172
  let pendingMatches;
2220
2173
  const previousMatches = this.state.matches;
2221
2174
  this.__store.batch(() => {
2222
- this.__store.setState(s => ({
2223
- ...s,
2224
- preloadMatches: s.preloadMatches.filter(d => {
2225
- return Date.now() - d.updatedAt < (this.options.defaultPreloadMaxAge ?? 3000);
2226
- })
2227
- }));
2175
+ // This is where all of the garbage collection magic happens
2176
+ this.__store.setState(s => {
2177
+ return {
2178
+ ...s,
2179
+ cachedMatches: s.cachedMatches.filter(d => {
2180
+ const route = this.looseRoutesById[d.routeId];
2181
+ return d.status !== 'error' && Date.now() - d.updatedAt < (route.options.gcTime ?? this.options.defaultGcTime ?? 5 * 60 * 1000);
2182
+ })
2183
+ };
2184
+ });
2228
2185
 
2229
2186
  // Match the routes
2230
2187
  pendingMatches = this.matchRoutes(next.pathname, next.search, {
@@ -2232,12 +2189,13 @@ class Router {
2232
2189
  });
2233
2190
 
2234
2191
  // Ingest the new matches
2192
+ // If a cached moved to pendingMatches, remove it from cachedMatches
2235
2193
  this.__store.setState(s => ({
2236
2194
  ...s,
2237
2195
  isLoading: true,
2238
2196
  location: next,
2239
2197
  pendingMatches,
2240
- preloadMatches: s.preloadMatches.filter(d => {
2198
+ cachedMatches: s.cachedMatches.filter(d => {
2241
2199
  return !pendingMatches.find(e => e.id === d.id);
2242
2200
  })
2243
2201
  }));
@@ -2259,19 +2217,23 @@ class Router {
2259
2217
  if (latestPromise = this.checkLatest(promise)) {
2260
2218
  return latestPromise;
2261
2219
  }
2262
- const exitingMatchIds = previousMatches.filter(id => !this.pendingMatches.includes(id));
2263
- const enteringMatchIds = this.pendingMatches.filter(id => !previousMatches.includes(id));
2264
- const stayingMatchIds = previousMatches.filter(id => this.pendingMatches.includes(id));
2220
+ const exitingMatches = previousMatches.filter(match => !pendingMatches.find(d => d.id === match.id));
2221
+ const enteringMatches = pendingMatches.filter(match => !previousMatches.find(d => d.id === match.id));
2222
+ const stayingMatches = previousMatches.filter(match => pendingMatches.find(d => d.id === match.id));
2223
+
2224
+ // Commit the pending matches. If a previous match was
2225
+ // removed, place it in the cachedMatches
2265
2226
  this.__store.setState(s => ({
2266
2227
  ...s,
2267
2228
  isLoading: false,
2268
2229
  matches: pendingMatches,
2269
- pendingMatches: undefined
2230
+ pendingMatches: undefined,
2231
+ cachedMatches: [...s.cachedMatches, ...exitingMatches.filter(d => d.status !== 'error')]
2270
2232
  }))
2271
2233
 
2272
2234
  //
2273
2235
  ;
2274
- [[exitingMatchIds, 'onLeave'], [enteringMatchIds, 'onEnter'], [stayingMatchIds, 'onStay']].forEach(([matches, hook]) => {
2236
+ [[exitingMatches, 'onLeave'], [enteringMatches, 'onEnter'], [stayingMatches, 'onStay']].forEach(([matches, hook]) => {
2275
2237
  matches.forEach(match => {
2276
2238
  this.looseRoutesById[match.routeId].options[hook]?.(match);
2277
2239
  });
@@ -2299,13 +2261,13 @@ class Router {
2299
2261
  let matches = this.matchRoutes(next.pathname, next.search, {
2300
2262
  throwOnError: true
2301
2263
  });
2302
- const loadedMatchIds = Object.fromEntries([...this.state.matches, ...(this.state.pendingMatches ?? []), ...this.state.preloadMatches]?.map(d => [d.id, true]));
2264
+ const loadedMatchIds = Object.fromEntries([...this.state.matches, ...(this.state.pendingMatches ?? []), ...this.state.cachedMatches]?.map(d => [d.id, true]));
2303
2265
  this.__store.batch(() => {
2304
2266
  matches.forEach(match => {
2305
2267
  if (!loadedMatchIds[match.id]) {
2306
2268
  this.__store.setState(s => ({
2307
2269
  ...s,
2308
- preloadMatches: [...s.preloadMatches, match]
2270
+ cachedMatches: [...s.cachedMatches, match]
2309
2271
  }));
2310
2272
  }
2311
2273
  });
@@ -2435,7 +2397,7 @@ function getInitialRouterState(location) {
2435
2397
  location,
2436
2398
  matches: [],
2437
2399
  pendingMatches: [],
2438
- preloadMatches: [],
2400
+ cachedMatches: [],
2439
2401
  lastUpdated: Date.now()
2440
2402
  };
2441
2403
  }