@tanstack/react-router 0.0.1-beta.234 → 0.0.1-beta.236

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.
@@ -15,6 +15,8 @@ export { default as invariant } from 'tiny-invariant';
15
15
  import warning from 'tiny-warning';
16
16
  export { default as warning } from 'tiny-warning';
17
17
  import * as React from 'react';
18
+ import { useStore } from '@tanstack/react-store';
19
+ import { Store } from '@tanstack/store';
18
20
 
19
21
  function CatchBoundary(props) {
20
22
  const errorComponent = props.errorComponent ?? ErrorComponent;
@@ -608,12 +610,10 @@ function createRouteMask(opts) {
608
610
 
609
611
  function Matches() {
610
612
  const {
611
- routesById,
612
- state
613
+ routesById
613
614
  } = useRouter();
614
- const {
615
- matches
616
- } = state;
615
+ const routerState = useRouterState();
616
+ const matches = routerState.pendingMatches?.some(d => d.showPending) ? routerState.pendingMatches : routerState.matches;
617
617
  const locationKey = useRouterState().location.state.key;
618
618
  const route = routesById[rootRouteId];
619
619
  const errorComponent = React.useCallback(props => {
@@ -759,7 +759,8 @@ function useMatch(opts) {
759
759
  const nearestMatchRouteId = nearestMatch?.routeId;
760
760
  const matchRouteId = useRouterState({
761
761
  select: state => {
762
- const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
762
+ const matches = state.pendingMatches?.some(d => d.showPending) ? state.pendingMatches : state.matches;
763
+ const match = opts?.from ? matches.find(d => d.routeId === opts?.from) : matches.find(d => d.id === nearestMatch.id);
763
764
  return match.routeId;
764
765
  }
765
766
  });
@@ -768,7 +769,8 @@ function useMatch(opts) {
768
769
  }
769
770
  const matchSelection = useRouterState({
770
771
  select: state => {
771
- const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
772
+ const matches = state.pendingMatches?.some(d => d.showPending) ? state.pendingMatches : state.matches;
773
+ const match = opts?.from ? matches.find(d => d.routeId === opts?.from) : matches.find(d => d.id === nearestMatch.id);
772
774
  invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
773
775
  return opts?.select ? opts.select(match) : match;
774
776
  }
@@ -780,7 +782,8 @@ function useMatches(opts) {
780
782
  const contextMatches = React.useContext(matchesContext);
781
783
  return useRouterState({
782
784
  select: state => {
783
- const matches = state.matches.slice(state.matches.findIndex(d => d.id === contextMatches[0]?.id));
785
+ let matches = state.pendingMatches?.some(d => d.showPending) ? state.pendingMatches : state.matches;
786
+ matches = matches.slice(matches.findIndex(d => d.id === contextMatches[0]?.id));
784
787
  return opts?.select ? opts.select(matches) : matches;
785
788
  }
786
789
  });
@@ -821,31 +824,29 @@ function RouterProvider({
821
824
  function RouterProviderInner({
822
825
  router
823
826
  }) {
824
- const [preState, setState] = React.useState(() => router.state);
825
827
  const [isTransitioning, startReactTransition] = React.useTransition();
826
- const isAnyTransitioning = isTransitioning || preState.matches.some(d => d.status === 'pending');
827
- const state = React.useMemo(() => ({
828
- ...preState,
829
- status: isAnyTransitioning ? 'pending' : 'idle',
830
- location: isTransitioning ? router.latestLocation : preState.location,
831
- pendingMatches: router.pendingMatches
832
- }), [preState, isTransitioning]);
833
- router.setState = setState;
834
- router.state = state;
835
828
  router.startReactTransition = startReactTransition;
829
+ React.useEffect(() => {
830
+ if (isTransitioning) {
831
+ router.__store.setState(s => ({
832
+ ...s,
833
+ isTransitioning
834
+ }));
835
+ }
836
+ }, [isTransitioning]);
836
837
  const tryLoad = () => {
837
- startReactTransition(() => {
838
- try {
839
- router.load();
840
- } catch (err) {
841
- console.error(err);
842
- }
843
- });
838
+ // startReactTransition(() => {
839
+ try {
840
+ router.load();
841
+ } catch (err) {
842
+ console.error(err);
843
+ }
844
+ // })
844
845
  };
845
846
  useLayoutEffect$1(() => {
846
847
  const unsub = router.history.subscribe(() => {
847
848
  router.latestLocation = router.parseLocation(router.latestLocation);
848
- if (state.location !== router.latestLocation) {
849
+ if (router.state.location !== router.latestLocation) {
849
850
  tryLoad();
850
851
  }
851
852
  });
@@ -855,7 +856,7 @@ function RouterProviderInner({
855
856
  hash: true,
856
857
  state: true
857
858
  });
858
- if (state.location.href !== nextLocation.href) {
859
+ if (router.state.location.href !== nextLocation.href) {
859
860
  router.commitLocation({
860
861
  ...nextLocation,
861
862
  replace: true
@@ -866,20 +867,21 @@ function RouterProviderInner({
866
867
  };
867
868
  }, [router.history]);
868
869
  useLayoutEffect$1(() => {
869
- if (!isTransitioning && state.resolvedLocation !== state.location) {
870
+ if (!isTransitioning && router.state.resolvedLocation !== router.state.location) {
870
871
  router.emit({
871
872
  type: 'onResolved',
872
- fromLocation: state.resolvedLocation,
873
- toLocation: state.location,
874
- pathChanged: state.location.href !== state.resolvedLocation?.href
873
+ fromLocation: router.state.resolvedLocation,
874
+ toLocation: router.state.location,
875
+ pathChanged: router.state.location.href !== router.state.resolvedLocation?.href
875
876
  });
876
877
  router.pendingMatches = [];
877
- setState(s => ({
878
+ router.__store.setState(s => ({
878
879
  ...s,
880
+ isTransitioning: false,
879
881
  resolvedLocation: s.location
880
882
  }));
881
883
  }
882
- });
884
+ }, [isTransitioning]);
883
885
  useLayoutEffect$1(() => {
884
886
  if (!window.__TSR_DEHYDRATED__) {
885
887
  tryLoad();
@@ -890,14 +892,11 @@ function RouterProviderInner({
890
892
  }, /*#__PURE__*/React.createElement(Matches, null));
891
893
  }
892
894
  function getRouteMatch(state, id) {
893
- return [...state.pendingMatches, ...state.matches].find(d => d.id === id);
895
+ return [...(state.pendingMatches ?? []), ...state.matches].find(d => d.id === id);
894
896
  }
895
897
  function useRouterState(opts) {
896
- const {
897
- state
898
- } = useRouter();
899
- // return useStore(router.__store, opts?.select as any)
900
- return opts?.select ? opts.select(state) : state;
898
+ const router = useRouter();
899
+ return useStore(router.__store, opts?.select);
901
900
  }
902
901
  function useRouter() {
903
902
  const resolvedContext = typeof document !== 'undefined' ? window.__TSR_ROUTER_CONTEXT__ || routerContext : routerContext;
@@ -1260,9 +1259,6 @@ class Router {
1260
1259
  // by the router provider once rendered. We provide these so that the
1261
1260
  // router can be used in a non-react environment if necessary
1262
1261
  startReactTransition = fn => fn();
1263
- setState = updater => {
1264
- this.state = functionalUpdate(updater, this.state);
1265
- };
1266
1262
  update = newOptions => {
1267
1263
  this.options = {
1268
1264
  ...this.options,
@@ -1277,10 +1273,20 @@ class Router {
1277
1273
  this.routeTree = this.options.routeTree;
1278
1274
  this.buildRouteTree();
1279
1275
  }
1280
- if (!this.state) {
1281
- this.state = getInitialRouterState(this.latestLocation);
1276
+ if (!this.__store) {
1277
+ this.__store = new Store(getInitialRouterState(this.latestLocation), {
1278
+ onUpdate: () => {
1279
+ this.__store.state = {
1280
+ ...this.state,
1281
+ status: this.state.isTransitioning || this.state.isLoading ? 'pending' : 'idle'
1282
+ };
1283
+ }
1284
+ });
1282
1285
  }
1283
1286
  };
1287
+ get state() {
1288
+ return this.__store.state;
1289
+ }
1284
1290
  buildRouteTree = () => {
1285
1291
  this.routesById = {};
1286
1292
  this.routesByPath = {};
@@ -1552,7 +1558,7 @@ class Router {
1552
1558
  getRouteMatch(this.state, id)?.abortController?.abort();
1553
1559
  };
1554
1560
  cancelMatches = () => {
1555
- this.state.matches.forEach(match => {
1561
+ this.state.pendingMatches?.forEach(match => {
1556
1562
  this.cancelMatch(match.id);
1557
1563
  });
1558
1564
  };
@@ -1743,6 +1749,12 @@ class Router {
1743
1749
  }) => {
1744
1750
  let latestPromise;
1745
1751
  let firstBadMatchIndex;
1752
+ const updatePendingMatch = match => {
1753
+ this.__store.setState(s => ({
1754
+ ...s,
1755
+ pendingMatches: s.pendingMatches?.map(d => d.id === match.id ? match : d)
1756
+ }));
1757
+ };
1746
1758
 
1747
1759
  // Check each match middleware to see if the route can be accessed
1748
1760
  try {
@@ -1899,12 +1911,10 @@ class Router {
1899
1911
  loadPromise
1900
1912
  };
1901
1913
  if (!preload) {
1902
- this.setState(s => ({
1903
- ...s,
1904
- matches: s.matches.map(d => d.id === match.id ? match : d)
1905
- }));
1914
+ updatePendingMatch(match);
1906
1915
  }
1907
1916
  let didShowPending = false;
1917
+ const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
1908
1918
  await new Promise(async resolve => {
1909
1919
  // If the route has a pending component and a pendingMs option,
1910
1920
  // forcefully show the pending component
@@ -1916,17 +1926,13 @@ class Router {
1916
1926
  ...match,
1917
1927
  showPending: true
1918
1928
  };
1919
- this.setState(s => ({
1920
- ...s,
1921
- matches: s.matches.map(d => d.id === match.id ? match : d)
1922
- }));
1929
+ updatePendingMatch(match);
1923
1930
  resolve();
1924
1931
  });
1925
1932
  }
1926
1933
  try {
1927
1934
  const loaderData = await loadPromise;
1928
1935
  if (latestPromise = checkLatest()) return await latestPromise;
1929
- const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
1930
1936
  if (didShowPending && pendingMinMs) {
1931
1937
  await new Promise(r => setTimeout(r, pendingMinMs));
1932
1938
  }
@@ -1956,12 +1962,19 @@ class Router {
1956
1962
  isFetching: false,
1957
1963
  updatedAt: Date.now()
1958
1964
  };
1965
+ } finally {
1966
+ // If we showed the pending component, that means
1967
+ // we already moved the pendingMatches to the matches
1968
+ // state, so we need to update that specific match
1969
+ if (didShowPending && pendingMinMs && match.showPending) {
1970
+ this.__store.setState(s => ({
1971
+ ...s,
1972
+ matches: s.matches?.map(d => d.id === match.id ? match : d)
1973
+ }));
1974
+ }
1959
1975
  }
1960
1976
  if (!preload) {
1961
- this.setState(s => ({
1962
- ...s,
1963
- matches: s.matches.map(d => d.id === match.id ? match : d)
1964
- }));
1977
+ updatePendingMatch(match);
1965
1978
  }
1966
1979
  resolve();
1967
1980
  });
@@ -1990,24 +2003,23 @@ class Router {
1990
2003
  });
1991
2004
 
1992
2005
  // Match the routes
1993
- let matches = this.matchRoutes(next.pathname, next.search, {
2006
+ let pendingMatches = this.matchRoutes(next.pathname, next.search, {
1994
2007
  debug: true
1995
2008
  });
1996
- this.pendingMatches = matches;
1997
2009
  const previousMatches = this.state.matches;
1998
2010
 
1999
2011
  // Ingest the new matches
2000
- this.setState(s => ({
2012
+ this.__store.setState(s => ({
2001
2013
  ...s,
2002
- // status: 'pending',
2014
+ isLoading: true,
2003
2015
  location: next,
2004
- matches
2016
+ pendingMatches
2005
2017
  }));
2006
2018
  try {
2007
2019
  try {
2008
2020
  // Load the matches
2009
2021
  await this.loadMatches({
2010
- matches,
2022
+ matches: pendingMatches,
2011
2023
  checkLatest: () => this.checkLatest(promise),
2012
2024
  invalidate: opts?.invalidate
2013
2025
  });
@@ -2022,14 +2034,13 @@ class Router {
2022
2034
  }
2023
2035
  const exitingMatchIds = previousMatches.filter(id => !this.pendingMatches.includes(id));
2024
2036
  const enteringMatchIds = this.pendingMatches.filter(id => !previousMatches.includes(id));
2025
- const stayingMatchIds = previousMatches.filter(id => this.pendingMatches.includes(id))
2026
-
2027
- // setState((s) => ({
2028
- // ...s,
2029
- // status: 'idle',
2030
- // resolvedLocation: s.location,
2031
- // matches,
2032
- // }))
2037
+ const stayingMatchIds = previousMatches.filter(id => this.pendingMatches.includes(id));
2038
+ this.__store.setState(s => ({
2039
+ ...s,
2040
+ isLoading: false,
2041
+ matches: pendingMatches,
2042
+ pendingMatches: undefined
2043
+ }))
2033
2044
 
2034
2045
  //
2035
2046
  ;
@@ -2184,9 +2195,6 @@ class Router {
2184
2195
  return false;
2185
2196
  }
2186
2197
  const baseLocation = opts?.pending ? this.latestLocation : this.state.resolvedLocation;
2187
-
2188
- // const baseLocation = state.resolvedLocation
2189
-
2190
2198
  if (!baseLocation) {
2191
2199
  return false;
2192
2200
  }
@@ -2258,7 +2266,7 @@ class Router {
2258
2266
  }
2259
2267
  return match;
2260
2268
  });
2261
- this.setState(s => {
2269
+ this.__store.setState(s => {
2262
2270
  return {
2263
2271
  ...s,
2264
2272
  matches: matches
@@ -2289,6 +2297,8 @@ class SearchParamError extends Error {}
2289
2297
  class PathParamError extends Error {}
2290
2298
  function getInitialRouterState(location) {
2291
2299
  return {
2300
+ isLoading: false,
2301
+ isTransitioning: false,
2292
2302
  status: 'idle',
2293
2303
  resolvedLocation: location,
2294
2304
  location,