@tanstack/react-router 0.0.1-beta.206 → 0.0.1-beta.208

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.
@@ -98,6 +98,7 @@ export declare function useParams<TRouteTree extends AnyRoute = RegisteredRouter
98
98
  export declare function useNavigate<TRouteTree extends AnyRoute = RegisteredRouter['routeTree'], TDefaultFrom extends RoutePaths<TRouteTree> = '/'>(defaultOpts?: {
99
99
  from?: TDefaultFrom;
100
100
  }): <TFrom extends RoutePaths<TRouteTree> = TDefaultFrom, TTo extends string = "", TMaskFrom extends RoutePaths<TRouteTree> = "/", TMaskTo extends string = "">(opts?: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> | undefined) => Promise<void>;
101
+ export declare function typedNavigate<TRouteTree extends AnyRoute = RegisteredRouter['routeTree'], TDefaultFrom extends RoutePaths<TRouteTree> = '/'>(navigate: (opts: NavigateOptions<any>) => Promise<void>): <TFrom extends RoutePaths<TRouteTree> = TDefaultFrom, TTo extends string = "", TMaskFrom extends RoutePaths<TRouteTree> = "/", TMaskTo extends string = "">(opts?: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> | undefined) => Promise<void>;
101
102
  export declare function useMatchRoute<TRouteTree extends AnyRoute = RegisteredRouter['routeTree']>(): <TFrom extends RoutePaths<TRouteTree> = "/", TTo extends string = "", TMaskFrom extends RoutePaths<TRouteTree> = "/", TMaskTo extends string = "", TResolved extends string = ResolveRelativePath<TFrom, NoInfer<TTo>>>(opts: MakeUseMatchRouteOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>) => false | RouteById<TRouteTree, TResolved>["types"]["allParams"];
102
103
  export declare function Matches(): JSX.Element;
103
104
  export declare function MatchRoute<TRouteTree extends AnyRoute = RegisteredRouter['routeTree'], TFrom extends RoutePaths<TRouteTree> = '/', TTo extends string = '', TMaskFrom extends RoutePaths<TRouteTree> = '/', TMaskTo extends string = ''>(props: MakeMatchRouteOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>): any;
@@ -2,7 +2,7 @@ import { RoutePaths } from './routeInfo';
2
2
  import { AnyRouter } from './router';
3
3
  import { AnyRouteMatch } from './RouteMatch';
4
4
  import { Expand, IsAny, NoInfer, PickRequired, UnionToIntersection, Assign } from './utils';
5
- import { ParsePathParams, ToSubOptions } from './link';
5
+ import { NavigateOptions, ParsePathParams, ToSubOptions } from './link';
6
6
  import { ErrorRouteComponent, PendingRouteComponent, RouteComponent } from './react';
7
7
  import { ParsedLocation } from './location';
8
8
  export declare const rootRouteId: "__root__";
@@ -46,6 +46,7 @@ type BeforeLoadFn<TFullSearchSchema extends Record<string, any>, TParentRoute ex
46
46
  params: TAllParams;
47
47
  meta: TParentRoute['types']['allMeta'];
48
48
  location: ParsedLocation;
49
+ navigate: (opts: NavigateOptions<AnyRoute>) => Promise<void>;
49
50
  }) => Promise<TRouteMeta> | TRouteMeta | void;
50
51
  export type UpdatableRouteOptions<TFullSearchSchema extends Record<string, any>, TAllParams extends AnyPathParams, TAllContext extends AnyContext> = {
51
52
  caseSensitive?: boolean;
@@ -84,6 +85,8 @@ export interface LoadFnContext<TAllParams = {}, TFullSearchSchema extends Record
84
85
  params: TAllParams;
85
86
  search: TFullSearchSchema;
86
87
  meta: Expand<Assign<TAllContext, TRouteMeta>>;
88
+ location: ParsedLocation<TFullSearchSchema>;
89
+ navigate: (opts: NavigateOptions<AnyRoute>) => Promise<void>;
87
90
  }
88
91
  export type SearchFilter<T, U = T> = (prev: T) => U;
89
92
  export type ResolveId<TParentRoute, TCustomId extends string, TPath extends string> = TParentRoute extends {
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { RouterHistory } from '@tanstack/history';
2
3
  import { AnySearchSchema, AnyRoute, AnyContext, AnyPathParams, RouteMask } from './route';
3
4
  import { FullSearchSchema } from './routeInfo';
@@ -7,9 +8,11 @@ import { RouteMatch } from './RouteMatch';
7
8
  import { ParsedLocation } from './location';
8
9
  import { LocationState } from './location';
9
10
  import { SearchSerializer, SearchParser } from './searchParams';
11
+ import { RouterContext } from './RouterProvider';
10
12
  declare global {
11
13
  interface Window {
12
14
  __TSR_DEHYDRATED__?: HydrationCtx;
15
+ __TSR_ROUTER_CONTEXT__?: React.Context<RouterContext<any>>;
13
16
  }
14
17
  }
15
18
  export interface Register {
@@ -996,6 +996,9 @@
996
996
 
997
997
  const preloadWarning = 'Error preloading route! ☝️';
998
998
  const routerContext = /*#__PURE__*/React__namespace.createContext(null);
999
+ if (typeof document !== 'undefined') {
1000
+ window.__TSR_ROUTER_CONTEXT__ = routerContext;
1001
+ }
999
1002
  function getInitialRouterState(location) {
1000
1003
  return {
1001
1004
  status: 'idle',
@@ -1023,6 +1026,10 @@
1023
1026
  const tempLocationKeyRef = React__namespace.useRef(`${Math.round(Math.random() * 10000000)}`);
1024
1027
  const resetNextScrollRef = React__namespace.useRef(false);
1025
1028
  const navigateTimeoutRef = React__namespace.useRef(null);
1029
+ const latestLoadPromiseRef = React__namespace.useRef(Promise.resolve());
1030
+ const checkLatest = promise => {
1031
+ return latestLoadPromiseRef.current !== promise ? latestLoadPromiseRef.current : undefined;
1032
+ };
1026
1033
  const parseLocation = useStableCallback(previousLocation => {
1027
1034
  const parse = ({
1028
1035
  pathname,
@@ -1146,7 +1153,6 @@
1146
1153
  d.child.rank = i;
1147
1154
  return d.child;
1148
1155
  }), [routesByPath]);
1149
- const latestLoadPromiseRef = React__namespace.useRef(Promise.resolve());
1150
1156
  const matchRoutes = useStableCallback((pathname, locationSearch, opts) => {
1151
1157
  let routeParams = {};
1152
1158
  let foundRoute = flatRoutes.find(route => {
@@ -1445,9 +1451,11 @@
1445
1451
  });
1446
1452
  });
1447
1453
  const loadMatches = useStableCallback(async ({
1454
+ checkLatest,
1448
1455
  matches,
1449
1456
  preload
1450
1457
  }) => {
1458
+ let latestPromise;
1451
1459
  let firstBadMatchIndex;
1452
1460
 
1453
1461
  // Check each match middleware to see if the route can be accessed
@@ -1490,7 +1498,12 @@
1490
1498
  params: match.params,
1491
1499
  preload: !!preload,
1492
1500
  meta: parentMeta,
1493
- location: state.location // TODO: This might need to be latestLocationRef.current...?
1501
+ location: state.location,
1502
+ // TODO: This might need to be latestLocationRef.current...?
1503
+ navigate: opts => navigate({
1504
+ ...opts,
1505
+ from: match.pathname
1506
+ })
1494
1507
  })) ?? {};
1495
1508
  const meta = {
1496
1509
  ...parentMeta,
@@ -1508,7 +1521,7 @@
1508
1521
  } catch (err) {
1509
1522
  if (isRedirect(err)) {
1510
1523
  if (!preload) navigate(err);
1511
- return;
1524
+ return matches;
1512
1525
  }
1513
1526
  throw err;
1514
1527
  }
@@ -1521,11 +1534,6 @@
1521
1534
  if (match.isFetching) {
1522
1535
  return getRouteMatch(state, match.id)?.loadPromise;
1523
1536
  }
1524
- const fetchedAt = Date.now();
1525
- const checkLatest = () => {
1526
- const latest = getRouteMatch(state, match.id);
1527
- return latest && latest.fetchedAt !== fetchedAt ? latest.loadPromise : undefined;
1528
- };
1529
1537
  const handleIfRedirect = err => {
1530
1538
  if (isRedirect(err)) {
1531
1539
  if (!preload) {
@@ -1536,7 +1544,6 @@
1536
1544
  return false;
1537
1545
  };
1538
1546
  const load = async () => {
1539
- let latestPromise;
1540
1547
  try {
1541
1548
  const componentsPromise = Promise.all(componentTypes.map(async type => {
1542
1549
  const component = route.options[type];
@@ -1550,7 +1557,12 @@
1550
1557
  preload: !!preload,
1551
1558
  parentMatchPromise,
1552
1559
  abortController: match.abortController,
1553
- meta: match.meta
1560
+ meta: match.meta,
1561
+ location: state.location,
1562
+ navigate: opts => navigate({
1563
+ ...opts,
1564
+ from: match.pathname
1565
+ })
1554
1566
  });
1555
1567
  await Promise.all([componentsPromise, loaderPromise]);
1556
1568
  if (latestPromise = checkLatest()) return await latestPromise;
@@ -1578,12 +1590,18 @@
1578
1590
  updatedAt: Date.now()
1579
1591
  };
1580
1592
  }
1593
+ if (!preload) {
1594
+ setState(s => ({
1595
+ ...s,
1596
+ matches: s.matches.map(d => d.id === match.id ? match : d)
1597
+ }));
1598
+ }
1581
1599
  };
1582
1600
  let loadPromise;
1583
1601
  matches[index] = match = {
1584
1602
  ...match,
1585
1603
  isFetching: true,
1586
- fetchedAt,
1604
+ fetchedAt: Date.now(),
1587
1605
  invalid: false
1588
1606
  };
1589
1607
  loadPromise = load();
@@ -1595,35 +1613,32 @@
1595
1613
  })());
1596
1614
  });
1597
1615
  await Promise.all(matchPromises);
1616
+ return matches;
1598
1617
  });
1599
- const load = useStableCallback(async opts => {
1618
+ const load = useStableCallback(async () => {
1600
1619
  const promise = new Promise(async (resolve, reject) => {
1620
+ const next = latestLocationRef.current;
1601
1621
  const prevLocation = state.resolvedLocation;
1602
- const pathDidChange = !!(opts?.next && prevLocation.href !== opts.next.href);
1622
+ const pathDidChange = !!(next && prevLocation.href !== next.href);
1603
1623
  let latestPromise;
1604
- const checkLatest = () => {
1605
- return latestLoadPromiseRef.current !== promise ? latestLoadPromiseRef.current : undefined;
1606
- };
1607
1624
 
1608
1625
  // Cancel any pending matches
1609
1626
  cancelMatches(state);
1610
1627
  router.emit({
1611
1628
  type: 'onBeforeLoad',
1612
1629
  from: prevLocation,
1613
- to: opts?.next ?? state.location,
1630
+ to: next ?? state.location,
1614
1631
  pathChanged: pathDidChange
1615
1632
  });
1616
- if (opts?.next) {
1617
- // Ingest the new location
1618
- setState(s => ({
1619
- ...s,
1620
- location: opts.next
1621
- }));
1622
- }
1633
+
1634
+ // Ingest the new location
1635
+ setState(s => ({
1636
+ ...s,
1637
+ location: next
1638
+ }));
1623
1639
 
1624
1640
  // Match the routes
1625
- const matches = matchRoutes(state.location.pathname, state.location.search, {
1626
- throwOnError: opts?.throwOnError,
1641
+ let matches = matchRoutes(next.pathname, next.search, {
1627
1642
  debug: true
1628
1643
  });
1629
1644
  setState(s => ({
@@ -1632,10 +1647,11 @@
1632
1647
  matches
1633
1648
  }));
1634
1649
  try {
1635
- // Load the matches
1636
1650
  try {
1651
+ // Load the matches
1637
1652
  await loadMatches({
1638
- matches
1653
+ matches,
1654
+ checkLatest: () => checkLatest(promise)
1639
1655
  });
1640
1656
  } catch (err) {
1641
1657
  // swallow this error, since we'll display the
@@ -1643,7 +1659,7 @@
1643
1659
  }
1644
1660
 
1645
1661
  // Only apply the latest transition
1646
- if (latestPromise = checkLatest()) {
1662
+ if (latestPromise = checkLatest(promise)) {
1647
1663
  return latestPromise;
1648
1664
  }
1649
1665
 
@@ -1680,13 +1696,13 @@
1680
1696
  router.emit({
1681
1697
  type: 'onLoad',
1682
1698
  from: prevLocation,
1683
- to: state.location,
1699
+ to: next,
1684
1700
  pathChanged: pathDidChange
1685
1701
  });
1686
1702
  resolve();
1687
1703
  } catch (err) {
1688
1704
  // Only apply the latest transition
1689
- if (latestPromise = checkLatest()) {
1705
+ if (latestPromise = checkLatest(promise)) {
1690
1706
  return latestPromise;
1691
1707
  }
1692
1708
  reject(err);
@@ -1695,13 +1711,6 @@
1695
1711
  latestLoadPromiseRef.current = promise;
1696
1712
  return latestLoadPromiseRef.current;
1697
1713
  });
1698
- const safeLoad = React__namespace.useCallback(async () => {
1699
- try {
1700
- return load();
1701
- } catch (err) {
1702
- // Don't do anything
1703
- }
1704
- }, []);
1705
1714
  const preloadRoute = useStableCallback(async (navigateOpts = state.location) => {
1706
1715
  let next = buildLocation(navigateOpts);
1707
1716
  let matches = matchRoutes(next.pathname, next.search, {
@@ -1709,7 +1718,8 @@
1709
1718
  });
1710
1719
  await loadMatches({
1711
1720
  matches,
1712
- preload: true
1721
+ preload: true,
1722
+ checkLatest: () => undefined
1713
1723
  });
1714
1724
  return [last(matches), matches];
1715
1725
  });
@@ -1822,10 +1832,13 @@
1822
1832
  const unsub = history.subscribe(() => {
1823
1833
  latestLocationRef.current = parseLocation(latestLocationRef.current);
1824
1834
  React__namespace.startTransition(() => {
1825
- setState(s => ({
1826
- ...s,
1827
- location: latestLocationRef.current
1828
- }));
1835
+ if (state.location !== latestLocationRef.current) {
1836
+ try {
1837
+ load();
1838
+ } catch (err) {
1839
+ console.error(err);
1840
+ }
1841
+ }
1829
1842
  });
1830
1843
  });
1831
1844
  const nextLocation = buildLocation({
@@ -1847,13 +1860,12 @@
1847
1860
  const initialLoad = React__namespace.useRef(true);
1848
1861
  if (initialLoad.current) {
1849
1862
  initialLoad.current = false;
1850
- safeLoad();
1851
- }
1852
- React__namespace.useLayoutEffect(() => {
1853
- if (state.resolvedLocation !== state.location) {
1854
- safeLoad();
1863
+ try {
1864
+ load();
1865
+ } catch (err) {
1866
+ console.error(err);
1855
1867
  }
1856
- }, [state.location]);
1868
+ }
1857
1869
  React__namespace.useMemo(() => [...state.matches, ...state.pendingMatches].some(d => d.isFetching), [state.matches, state.pendingMatches]);
1858
1870
  const matchRoute = useStableCallback((state, location, opts) => {
1859
1871
  location = {
@@ -2058,7 +2070,8 @@
2058
2070
  }
2059
2071
  const matchesContext = /*#__PURE__*/React__namespace.createContext(null);
2060
2072
  function useRouter() {
2061
- const value = React__namespace.useContext(routerContext);
2073
+ const resolvedContext = window.__TSR_ROUTER_CONTEXT__ || routerContext;
2074
+ const value = React__namespace.useContext(resolvedContext);
2062
2075
  warning(value, 'useRouter must be used inside a <RouterProvider> component!');
2063
2076
  return value;
2064
2077
  }
@@ -2136,6 +2149,9 @@
2136
2149
  });
2137
2150
  }, []);
2138
2151
  }
2152
+ function typedNavigate(navigate) {
2153
+ return navigate;
2154
+ }
2139
2155
  function useMatchRoute() {
2140
2156
  const {
2141
2157
  state,
@@ -2624,6 +2640,7 @@
2624
2640
  exports.trimPath = trimPath;
2625
2641
  exports.trimPathLeft = trimPathLeft;
2626
2642
  exports.trimPathRight = trimPathRight;
2643
+ exports.typedNavigate = typedNavigate;
2627
2644
  exports.useBlocker = useBlocker;
2628
2645
  exports.useLinkProps = useLinkProps;
2629
2646
  exports.useMatch = useMatch;