@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.
@@ -31,7 +31,6 @@ class Router {
31
31
  navigateTimeout = null;
32
32
  latestLoadPromise = Promise.resolve();
33
33
  subscribers = new Set();
34
- pendingMatches = [];
35
34
  injectedHtml = [];
36
35
 
37
36
  // Must build in constructor
@@ -355,7 +354,6 @@ class Router {
355
354
  routeContext: undefined,
356
355
  context: undefined,
357
356
  abortController: new AbortController(),
358
- shouldReloadDeps: undefined,
359
357
  fetchCount: 0,
360
358
  cause,
361
359
  loaderDeps
@@ -567,9 +565,10 @@ class Router {
567
565
  let latestPromise;
568
566
  let firstBadMatchIndex;
569
567
  const updateMatch = match => {
570
- // const isPreload = this.state.preloadMatches.find((d) => d.id === match.id)
568
+ // const isPreload = this.state.cachedMatches.find((d) => d.id === match.id)
571
569
  const isPending = this.state.pendingMatches?.find(d => d.id === match.id);
572
- const matchesKey = preload ? 'preloadMatches' : isPending ? 'pendingMatches' : 'matches';
570
+ const isMatched = this.state.matches.find(d => d.id === match.id);
571
+ const matchesKey = isPending ? 'pendingMatches' : isMatched ? 'matches' : 'cachedMatches';
573
572
  this.__store.setState(s => ({
574
573
  ...s,
575
574
  [matchesKey]: s[matchesKey]?.map(d => d.id === match.id ? match : d)
@@ -694,65 +693,24 @@ class Router {
694
693
  }),
695
694
  cause: preload ? 'preload' : match.cause
696
695
  };
697
-
698
- // Default to reloading the route all the time
699
- let shouldLoad = true;
700
- const shouldReloadFn = route.options.shouldReload;
701
- let shouldReloadDeps = typeof shouldReloadFn === 'function' ? shouldReloadFn(loaderContext) : !!(shouldReloadFn ?? true);
702
- const compareDeps = () => {
703
- if (typeof shouldReloadDeps === 'object') {
704
- // compare the deps to see if they've changed
705
- shouldLoad = !utils.deepEqual(shouldReloadDeps, match.shouldReloadDeps);
706
- } else {
707
- shouldLoad = !!shouldReloadDeps;
708
- }
709
- };
710
-
711
- // If it's the first preload, or the route is entering, or we're
712
- // invalidating, we definitely need to load the route
713
- if (invalidate) ; else if (preload) {
714
- if (!match.fetchCount) ; else {
715
- compareDeps();
716
- }
717
- } else if (match.cause === 'enter') {
718
- if (!match.fetchCount) ; else {
719
- compareDeps();
720
- }
721
- } else {
722
- compareDeps();
723
- }
724
- if (typeof shouldReloadDeps === 'object') {
725
- matches[index] = match = {
726
- ...match,
727
- shouldReloadDeps
728
- };
696
+ if (match.fetchCount && match.status === 'success') {
697
+ resolve();
729
698
  }
730
699
 
731
- // If the user doesn't want the route to reload, just
732
- // resolve with the existing loader data
733
-
734
- if (!shouldLoad) {
735
- loadPromise = Promise.resolve(match.loaderData);
736
- } else {
737
- if (match.fetchCount && match.status === 'success') {
738
- resolve();
700
+ // Otherwise, load the route
701
+ matches[index] = match = {
702
+ ...match,
703
+ isFetching: true,
704
+ fetchCount: match.fetchCount + 1
705
+ };
706
+ const componentsPromise = Promise.all(componentTypes.map(async type => {
707
+ const component = route.options[type];
708
+ if (component?.preload) {
709
+ await component.preload();
739
710
  }
740
-
741
- // Otherwise, load the route
742
- matches[index] = match = {
743
- ...match,
744
- isFetching: true,
745
- fetchCount: match.fetchCount + 1
746
- };
747
- const componentsPromise = Promise.all(componentTypes.map(async type => {
748
- const component = route.options[type];
749
- if (component?.preload) {
750
- await component.preload();
751
- }
752
- }));
753
- const loaderPromise = route.options.loader?.(loaderContext);
754
- loadPromise = Promise.all([componentsPromise, loaderPromise]).then(d => d[1]);
755
- }
711
+ }));
712
+ const loaderPromise = route.options.loader?.(loaderContext);
713
+ loadPromise = Promise.all([componentsPromise, loaderPromise]).then(d => d[1]);
756
714
  }
757
715
  matches[index] = match = {
758
716
  ...match,
@@ -791,24 +749,23 @@ class Router {
791
749
  ...match,
792
750
  error,
793
751
  status: 'error',
794
- isFetching: false,
795
- updatedAt: Date.now()
752
+ isFetching: false
796
753
  };
797
- } finally {
798
- // If we showed the pending component, that means
799
- // we already moved the pendingMatches to the matches
800
- // state, so we need to update that specific match
801
- if (didShowPending && pendingMinMs && match.showPending) {
802
- updateMatch(match);
803
- }
804
754
  }
805
755
  updateMatch(match);
806
756
  };
807
- if (match.fetchCount && match.status === 'success') {
808
- // Background Fetching
809
- fetch();
757
+
758
+ // This is where all of the stale-while-revalidate magic happens
759
+ const age = Date.now() - match.updatedAt;
760
+ let staleAge = preload ? route.options.preloadStaleTime ?? this.options.defaultPreloadStaleTime ?? 30_000 // 30 seconds for preloads by default
761
+ : route.options.staleTime ?? this.options.defaultStaleTime ?? 0;
762
+ if (match.status === 'success') {
763
+ // Background Fetching, no need to wait
764
+ if (age > staleAge) {
765
+ fetch();
766
+ }
810
767
  } else {
811
- // Critical Fetching
768
+ // Critical Fetching, we need to await
812
769
 
813
770
  // If we need to potentially show the pending component,
814
771
  // start a timer to show it after the pendingMs
@@ -827,9 +784,6 @@ class Router {
827
784
  await fetch();
828
785
  }
829
786
  resolve();
830
- // No Fetching
831
-
832
- resolve();
833
787
  }));
834
788
  });
835
789
  await Promise.all(matchPromises);
@@ -856,12 +810,16 @@ class Router {
856
810
  let pendingMatches;
857
811
  const previousMatches = this.state.matches;
858
812
  this.__store.batch(() => {
859
- this.__store.setState(s => ({
860
- ...s,
861
- preloadMatches: s.preloadMatches.filter(d => {
862
- return Date.now() - d.updatedAt < (this.options.defaultPreloadMaxAge ?? 3000);
863
- })
864
- }));
813
+ // This is where all of the garbage collection magic happens
814
+ this.__store.setState(s => {
815
+ return {
816
+ ...s,
817
+ cachedMatches: s.cachedMatches.filter(d => {
818
+ const route = this.looseRoutesById[d.routeId];
819
+ return d.status !== 'error' && Date.now() - d.updatedAt < (route.options.gcTime ?? this.options.defaultGcTime ?? 5 * 60 * 1000);
820
+ })
821
+ };
822
+ });
865
823
 
866
824
  // Match the routes
867
825
  pendingMatches = this.matchRoutes(next.pathname, next.search, {
@@ -869,12 +827,13 @@ class Router {
869
827
  });
870
828
 
871
829
  // Ingest the new matches
830
+ // If a cached moved to pendingMatches, remove it from cachedMatches
872
831
  this.__store.setState(s => ({
873
832
  ...s,
874
833
  isLoading: true,
875
834
  location: next,
876
835
  pendingMatches,
877
- preloadMatches: s.preloadMatches.filter(d => {
836
+ cachedMatches: s.cachedMatches.filter(d => {
878
837
  return !pendingMatches.find(e => e.id === d.id);
879
838
  })
880
839
  }));
@@ -896,19 +855,23 @@ class Router {
896
855
  if (latestPromise = this.checkLatest(promise)) {
897
856
  return latestPromise;
898
857
  }
899
- const exitingMatchIds = previousMatches.filter(id => !this.pendingMatches.includes(id));
900
- const enteringMatchIds = this.pendingMatches.filter(id => !previousMatches.includes(id));
901
- const stayingMatchIds = previousMatches.filter(id => this.pendingMatches.includes(id));
858
+ const exitingMatches = previousMatches.filter(match => !pendingMatches.find(d => d.id === match.id));
859
+ const enteringMatches = pendingMatches.filter(match => !previousMatches.find(d => d.id === match.id));
860
+ const stayingMatches = previousMatches.filter(match => pendingMatches.find(d => d.id === match.id));
861
+
862
+ // Commit the pending matches. If a previous match was
863
+ // removed, place it in the cachedMatches
902
864
  this.__store.setState(s => ({
903
865
  ...s,
904
866
  isLoading: false,
905
867
  matches: pendingMatches,
906
- pendingMatches: undefined
868
+ pendingMatches: undefined,
869
+ cachedMatches: [...s.cachedMatches, ...exitingMatches.filter(d => d.status !== 'error')]
907
870
  }))
908
871
 
909
872
  //
910
873
  ;
911
- [[exitingMatchIds, 'onLeave'], [enteringMatchIds, 'onEnter'], [stayingMatchIds, 'onStay']].forEach(([matches, hook]) => {
874
+ [[exitingMatches, 'onLeave'], [enteringMatches, 'onEnter'], [stayingMatches, 'onStay']].forEach(([matches, hook]) => {
912
875
  matches.forEach(match => {
913
876
  this.looseRoutesById[match.routeId].options[hook]?.(match);
914
877
  });
@@ -936,13 +899,13 @@ class Router {
936
899
  let matches = this.matchRoutes(next.pathname, next.search, {
937
900
  throwOnError: true
938
901
  });
939
- const loadedMatchIds = Object.fromEntries([...this.state.matches, ...(this.state.pendingMatches ?? []), ...this.state.preloadMatches]?.map(d => [d.id, true]));
902
+ const loadedMatchIds = Object.fromEntries([...this.state.matches, ...(this.state.pendingMatches ?? []), ...this.state.cachedMatches]?.map(d => [d.id, true]));
940
903
  this.__store.batch(() => {
941
904
  matches.forEach(match => {
942
905
  if (!loadedMatchIds[match.id]) {
943
906
  this.__store.setState(s => ({
944
907
  ...s,
945
- preloadMatches: [...s.preloadMatches, match]
908
+ cachedMatches: [...s.cachedMatches, match]
946
909
  }));
947
910
  }
948
911
  });
@@ -1072,7 +1035,7 @@ function getInitialRouterState(location) {
1072
1035
  location,
1073
1036
  matches: [],
1074
1037
  pendingMatches: [],
1075
- preloadMatches: [],
1038
+ cachedMatches: [],
1076
1039
  lastUpdated: Date.now()
1077
1040
  };
1078
1041
  }