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

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.
@@ -26,6 +26,7 @@ export interface RouteMatch<TRouteTree extends AnyRoute = AnyRoute, TRouteId ext
26
26
  abortController: AbortController;
27
27
  cause: 'preload' | 'enter' | 'stay';
28
28
  loaderDeps: RouteById<TRouteTree, TRouteId>['types']['loaderDeps'];
29
+ invalid: boolean;
29
30
  }
30
31
  export type AnyRouteMatch = RouteMatch<any, any>;
31
32
  export declare function Matches(): React.JSX.Element;
@@ -34,6 +34,7 @@ export type ParamsFallback<TPath extends string, TParams> = unknown extends TPar
34
34
  export type BaseRouteOptions<TParentRoute extends AnyRoute = AnyRoute, TCustomId extends string = string, TPath extends string = string, TSearchSchema extends Record<string, any> = {}, TFullSearchSchema extends Record<string, any> = TSearchSchema, TParams extends AnyPathParams = {}, TAllParams = ParamsFallback<TPath, TParams>, TRouteContext extends RouteContext = RouteContext, TAllContext extends Record<string, any> = AnyContext, TLoaderDeps extends Record<string, any> = {}, TLoaderData extends any = unknown> = RoutePathOptions<TCustomId, TPath> & {
35
35
  getParentRoute: () => TParentRoute;
36
36
  validateSearch?: SearchSchemaValidator<TSearchSchema>;
37
+ shouldReload?: boolean | ((match: LoaderFnContext<TAllParams, TFullSearchSchema, TAllContext, TRouteContext>) => any);
37
38
  } & (keyof PickRequired<RouteContext> extends never ? {
38
39
  beforeLoad?: BeforeLoadFn<TFullSearchSchema, TParentRoute, TAllParams, TRouteContext>;
39
40
  } : {
@@ -162,16 +162,13 @@ export declare class Router<TRouteTree extends AnyRoute = AnyRoute, TDehydrated
162
162
  commitLocation: ({ startTransition, ...next }: ParsedLocation & CommitLocationOptions) => Promise<void>;
163
163
  buildAndCommitLocation: ({ replace, resetScroll, startTransition, ...rest }?: BuildNextOptions & CommitLocationOptions) => Promise<void>;
164
164
  navigate: NavigateFn<TRouteTree>;
165
- loadMatches: ({ checkLatest, matches, preload, invalidate, }: {
165
+ loadMatches: ({ checkLatest, matches, preload, }: {
166
166
  checkLatest: () => Promise<void> | undefined;
167
167
  matches: AnyRouteMatch[];
168
168
  preload?: boolean | undefined;
169
- invalidate?: boolean | undefined;
170
169
  }) => Promise<RouteMatch[]>;
171
- invalidate: () => Promise<void>;
172
- load: (opts?: {
173
- invalidate?: boolean;
174
- }) => Promise<void>;
170
+ invalidate: () => void;
171
+ load: () => Promise<void>;
175
172
  preloadRoute: (navigateOpts?: ToOptions<TRouteTree>) => Promise<RouteMatch<AnyRoute, any>[]>;
176
173
  matchRoute: MatchRouteFn<TRouteTree>;
177
174
  injectHtml: (html: string | (() => Promise<string> | string)) => Promise<void>;
@@ -2326,7 +2326,8 @@
2326
2326
  abortController: new AbortController(),
2327
2327
  fetchCount: 0,
2328
2328
  cause,
2329
- loaderDeps
2329
+ loaderDeps,
2330
+ invalid: false
2330
2331
  };
2331
2332
 
2332
2333
  // Regardless of whether we're reusing an existing match or creating
@@ -2529,8 +2530,7 @@
2529
2530
  loadMatches = async ({
2530
2531
  checkLatest,
2531
2532
  matches,
2532
- preload,
2533
- invalidate
2533
+ preload
2534
2534
  }) => {
2535
2535
  let latestPromise;
2536
2536
  let firstBadMatchIndex;
@@ -2645,24 +2645,27 @@
2645
2645
  const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
2646
2646
  const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
2647
2647
  const shouldPending = !preload && pendingMs && (route.options.pendingComponent ?? this.options.defaultPendingComponent);
2648
+ const loaderContext = {
2649
+ params: match.params,
2650
+ deps: match.loaderDeps,
2651
+ preload: !!preload,
2652
+ parentMatchPromise,
2653
+ abortController: match.abortController,
2654
+ context: match.context,
2655
+ location: this.state.location,
2656
+ navigate: opts => this.navigate({
2657
+ ...opts,
2658
+ from: match.pathname
2659
+ }),
2660
+ cause: preload ? 'preload' : match.cause
2661
+ };
2648
2662
  const fetch = async () => {
2649
2663
  if (match.isFetching) {
2650
2664
  loadPromise = getRouteMatch(this.state, match.id)?.loadPromise;
2651
2665
  } else {
2652
- const loaderContext = {
2653
- params: match.params,
2654
- deps: match.loaderDeps,
2655
- preload: !!preload,
2656
- parentMatchPromise,
2657
- abortController: match.abortController,
2658
- context: match.context,
2659
- location: this.state.location,
2660
- navigate: opts => this.navigate({
2661
- ...opts,
2662
- from: match.pathname
2663
- }),
2664
- cause: preload ? 'preload' : match.cause
2665
- };
2666
+ // If the user doesn't want the route to reload, just
2667
+ // resolve with the existing loader data
2668
+
2666
2669
  if (match.fetchCount && match.status === 'success') {
2667
2670
  resolve();
2668
2671
  }
@@ -2729,14 +2732,15 @@
2729
2732
  const age = Date.now() - match.updatedAt;
2730
2733
  let staleAge = preload ? route.options.preloadStaleTime ?? this.options.defaultPreloadStaleTime ?? 30_000 // 30 seconds for preloads by default
2731
2734
  : route.options.staleTime ?? this.options.defaultStaleTime ?? 0;
2732
- if (match.status === 'success') {
2733
- // Background Fetching, no need to wait
2734
- if (age > staleAge) {
2735
- fetch();
2736
- }
2737
- } else {
2738
- // Critical Fetching, we need to await
2739
2735
 
2736
+ // Default to reloading the route all the time
2737
+ let shouldReload;
2738
+ const shouldReloadOption = route.options.shouldReload;
2739
+
2740
+ // Allow shouldReload to get the last say,
2741
+ // if provided.
2742
+ shouldReload = typeof shouldReloadOption === 'function' ? shouldReloadOption(loaderContext) : shouldReloadOption;
2743
+ if (match.status !== 'success') {
2740
2744
  // If we need to potentially show the pending component,
2741
2745
  // start a timer to show it after the pendingMs
2742
2746
  if (shouldPending) {
@@ -2751,7 +2755,12 @@
2751
2755
  resolve();
2752
2756
  });
2753
2757
  }
2758
+
2759
+ // Critical Fetching, we need to await
2754
2760
  await fetch();
2761
+ } else if (match.invalid || (shouldReload ?? age > staleAge)) {
2762
+ // Background Fetching, no need to wait
2763
+ fetch();
2755
2764
  }
2756
2765
  resolve();
2757
2766
  }));
@@ -2759,10 +2768,20 @@
2759
2768
  await Promise.all(matchPromises);
2760
2769
  return matches;
2761
2770
  };
2762
- invalidate = () => this.load({
2763
- invalidate: true
2764
- });
2765
- load = async opts => {
2771
+ invalidate = () => {
2772
+ const invalidate = d => ({
2773
+ ...d,
2774
+ invalid: true
2775
+ });
2776
+ this.__store.setState(s => ({
2777
+ ...s,
2778
+ matches: s.matches.map(invalidate),
2779
+ cachedMatches: s.cachedMatches.map(invalidate),
2780
+ pendingMatches: s.pendingMatches?.map(invalidate)
2781
+ }));
2782
+ this.load();
2783
+ };
2784
+ load = async () => {
2766
2785
  const promise = new Promise(async (resolve, reject) => {
2767
2786
  const next = this.latestLocation;
2768
2787
  const prevLocation = this.state.resolvedLocation;
@@ -2813,8 +2832,7 @@
2813
2832
  // Load the matches
2814
2833
  await this.loadMatches({
2815
2834
  matches: pendingMatches,
2816
- checkLatest: () => this.checkLatest(promise),
2817
- invalidate: opts?.invalidate
2835
+ checkLatest: () => this.checkLatest(promise)
2818
2836
  });
2819
2837
  } catch (err) {
2820
2838
  // swallow this error, since we'll display the