@tanstack/react-router 0.0.1-beta.226 → 0.0.1-beta.228

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.
@@ -637,7 +637,6 @@ function Matches() {
637
637
  matches: matches
638
638
  }) : null));
639
639
  }
640
- const defaultPending = () => null;
641
640
  function SafeFragment(props) {
642
641
  return /*#__PURE__*/React.createElement(React.Fragment, null, props.children);
643
642
  }
@@ -652,9 +651,9 @@ function Match({
652
651
  const routeId = match?.routeId;
653
652
  const route = routesById[routeId];
654
653
  const locationKey = useRouterState().location.state?.key;
655
- const PendingComponent = route.options.pendingComponent ?? options.defaultPendingComponent ?? defaultPending;
654
+ const PendingComponent = route.options.pendingComponent ?? options.defaultPendingComponent;
656
655
  const routeErrorComponent = route.options.errorComponent ?? options.defaultErrorComponent ?? ErrorComponent;
657
- const ResolvedSuspenseBoundary = route.options.wrapInSuspense ? React.Suspense : SafeFragment;
656
+ const ResolvedSuspenseBoundary = route.options.wrapInSuspense ?? PendingComponent ? React.Suspense : SafeFragment;
658
657
  const errorComponent = routeErrorComponent ? React.useCallback(props => {
659
658
  return /*#__PURE__*/React.createElement(routeErrorComponent, {
660
659
  ...props,
@@ -664,6 +663,7 @@ function Match({
664
663
  useParams: route.useParams
665
664
  });
666
665
  }, [route]) : undefined;
666
+ const ResolvedCatchBoundary = errorComponent ? CatchBoundary : SafeFragment;
667
667
  return /*#__PURE__*/React.createElement(matchesContext.Provider, {
668
668
  value: matches
669
669
  }, /*#__PURE__*/React.createElement(ResolvedSuspenseBoundary, {
@@ -673,7 +673,7 @@ function Match({
673
673
  useSearch: route.useSearch,
674
674
  useParams: route.useParams
675
675
  })
676
- }, errorComponent ? /*#__PURE__*/React.createElement(CatchBoundary, {
676
+ }, /*#__PURE__*/React.createElement(ResolvedCatchBoundary, {
677
677
  resetKey: locationKey,
678
678
  errorComponent: errorComponent,
679
679
  onCatch: () => {
@@ -681,8 +681,6 @@ function Match({
681
681
  }
682
682
  }, /*#__PURE__*/React.createElement(MatchInner, {
683
683
  match: match
684
- })) : /*#__PURE__*/React.createElement(SafeFragment, null, /*#__PURE__*/React.createElement(MatchInner, {
685
- match: match
686
684
  }))));
687
685
  }
688
686
  function MatchInner({
@@ -786,31 +784,6 @@ function useLoaderData(opts) {
786
784
  return typeof opts.select === 'function' ? opts.select(match?.loaderData) : match?.loaderData;
787
785
  }
788
786
 
789
- // export type RouterContext<
790
- // TRouteTree extends AnyRoute,
791
- // // TDehydrated extends Record<string, any>,
792
- // > = {
793
- // buildLink: BuildLinkFn<TRouteTree>
794
- // state: RouterState<TRouteTree>
795
- // navigate: NavigateFn<TRouteTree>
796
- // matchRoute: MatchRouteFn<TRouteTree>
797
- // routeTree: TRouteTree
798
- // routesById: RoutesById<TRouteTree>
799
- // options: RouterOptions<TRouteTree>
800
- // history: RouterHistory
801
- // load: LoadFn
802
- // buildLocation: BuildLocationFn<TRouteTree>
803
- // subscribe: Router<TRouteTree>['subscribe']
804
- // resetNextScrollRef: React.MutableRefObject<boolean>
805
- // injectedHtmlRef: React.MutableRefObject<InjectedHtmlEntry[]>
806
- // injectHtml: (entry: InjectedHtmlEntry) => void
807
- // dehydrateData: <T>(
808
- // key: any,
809
- // getData: T | (() => Promise<T> | T),
810
- // ) => () => void
811
- // hydrateData: <T>(key: any) => T | undefined
812
- // }
813
-
814
787
  const routerContext = /*#__PURE__*/React.createContext(null);
815
788
  if (typeof document !== 'undefined') {
816
789
  window.__TSR_ROUTER_CONTEXT__ = routerContext;
@@ -1484,38 +1457,66 @@ class Router {
1484
1457
  }
1485
1458
  return;
1486
1459
  });
1487
- const matches = matchedRoutes.map((route, index) => {
1460
+ const matches = [];
1461
+ matchedRoutes.forEach((route, index) => {
1462
+ // Take each matched route and resolve + validate its search params
1463
+ // This has to happen serially because each route's search params
1464
+ // can depend on the parent route's search params
1465
+ // It must also happen before we create the match so that we can
1466
+ // pass the search params to the route's potential key function
1467
+ // which is used to uniquely identify the route match in state
1468
+
1469
+ const parentMatch = matches[index - 1];
1470
+ const [preMatchSearch, searchError] = (() => {
1471
+ // Validate the search params and stabilize them
1472
+ const parentSearch = parentMatch?.search ?? locationSearch;
1473
+ try {
1474
+ const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
1475
+ let search = validator?.(parentSearch) ?? {};
1476
+ return [{
1477
+ ...parentSearch,
1478
+ ...search
1479
+ }, undefined];
1480
+ } catch (err) {
1481
+ const searchError = new SearchParamError(err.message, {
1482
+ cause: err
1483
+ });
1484
+ if (opts?.throwOnError) {
1485
+ throw searchError;
1486
+ }
1487
+ return [parentSearch, searchError];
1488
+ }
1489
+ })();
1488
1490
  const interpolatedPath = interpolatePath(route.path, routeParams);
1489
- const matchId = interpolatePath(route.id, routeParams, true);
1491
+ const matchId = interpolatePath(route.id, routeParams, true) + (route.options.key?.({
1492
+ search: preMatchSearch,
1493
+ location: this.state.location
1494
+ }) ?? '');
1490
1495
 
1491
1496
  // Waste not, want not. If we already have a match for this route,
1492
1497
  // reuse it. This is important for layout routes, which might stick
1493
1498
  // around between navigation actions that only change leaf routes.
1494
1499
  const existingMatch = getRouteMatch(this.state, matchId);
1495
1500
  const cause = this.state.matches.find(d => d.id === matchId) ? 'stay' : 'enter';
1496
- if (existingMatch) {
1497
- return {
1498
- ...existingMatch,
1499
- cause
1500
- };
1501
- }
1502
1501
 
1503
1502
  // Create a fresh route match
1504
1503
  const hasLoaders = !!(route.options.loader || componentTypes.some(d => route.options[d]?.preload));
1505
- const routeMatch = {
1504
+ const match = existingMatch ? {
1505
+ ...existingMatch,
1506
+ cause
1507
+ } : {
1506
1508
  id: matchId,
1507
1509
  routeId: route.id,
1508
1510
  params: routeParams,
1509
1511
  pathname: joinPaths([this.basepath, interpolatedPath]),
1510
1512
  updatedAt: Date.now(),
1511
- routeSearch: {},
1512
1513
  search: {},
1514
+ searchError: undefined,
1513
1515
  status: hasLoaders ? 'pending' : 'success',
1514
1516
  isFetching: false,
1515
1517
  invalid: false,
1516
1518
  error: undefined,
1517
1519
  paramsError: parseErrors[index],
1518
- searchError: undefined,
1519
1520
  loadPromise: Promise.resolve(),
1520
1521
  context: undefined,
1521
1522
  abortController: new AbortController(),
@@ -1523,46 +1524,13 @@ class Router {
1523
1524
  fetchedAt: 0,
1524
1525
  cause
1525
1526
  };
1526
- return routeMatch;
1527
- });
1528
1527
 
1529
- // Take each match and resolve its search params and context
1530
- // This has to happen after the matches are created or found
1531
- // so that we can use the parent match's search params and context
1532
- matches.forEach((match, i) => {
1533
- const parentMatch = matches[i - 1];
1534
- const route = this.looseRoutesById[match.routeId];
1535
- const searchInfo = (() => {
1536
- // Validate the search params and stabilize them
1537
- const parentSearchInfo = {
1538
- search: parentMatch?.search ?? locationSearch,
1539
- routeSearch: parentMatch?.routeSearch ?? locationSearch
1540
- };
1541
- try {
1542
- const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
1543
- let routeSearch = validator?.(parentSearchInfo.search) ?? {};
1544
- let search = {
1545
- ...parentSearchInfo.search,
1546
- ...routeSearch
1547
- };
1548
- routeSearch = replaceEqualDeep(match.routeSearch, routeSearch);
1549
- search = replaceEqualDeep(match.search, search);
1550
- return {
1551
- routeSearch,
1552
- search,
1553
- searchDidChange: match.routeSearch !== routeSearch
1554
- };
1555
- } catch (err) {
1556
- match.searchError = new SearchParamError(err.message, {
1557
- cause: err
1558
- });
1559
- if (opts?.throwOnError) {
1560
- throw match.searchError;
1561
- }
1562
- return parentSearchInfo;
1563
- }
1564
- })();
1565
- Object.assign(match, searchInfo);
1528
+ // Regardless of whether we're reusing an existing match or creating
1529
+ // a new one, we need to update the match's search params
1530
+ match.search = replaceEqualDeep(match.search, preMatchSearch);
1531
+ // And also update the searchError if there is one
1532
+ match.searchError = searchError;
1533
+ matches.push(match);
1566
1534
  });
1567
1535
  return matches;
1568
1536
  };