@tanstack/react-router 0.0.1-beta.60 → 0.0.1-beta.62

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.
@@ -89,9 +89,9 @@
89
89
  const previous = this.state;
90
90
  this.state = this.options?.updateFn ? this.options.updateFn(previous)(updater) : updater(previous);
91
91
  if (this.state === previous) return;
92
+ this.options?.onUpdate?.(this.state, previous);
92
93
  this.queue.push(() => {
93
94
  this.listeners.forEach(listener => listener(this.state, previous));
94
- this.options?.onUpdate?.(this.state, previous);
95
95
  });
96
96
  this.#flush();
97
97
  };
@@ -577,7 +577,9 @@
577
577
  this.options = options || {};
578
578
  this.isRoot = !options?.getParentRoute;
579
579
  }
580
- init = () => {
580
+ init = opts => {
581
+ this.originalIndex = opts.originalIndex;
582
+ this.router = opts.router;
581
583
  const allOptions = this.options;
582
584
  const isRoot = !allOptions?.path && !allOptions?.id;
583
585
  const parent = this.options?.getParentRoute?.();
@@ -603,6 +605,7 @@
603
605
  id = joinPaths(['/', id]);
604
606
  }
605
607
  const fullPath = id === rootRouteId ? '/' : trimPathRight(joinPaths([parent.fullPath, path]));
608
+ this.path = path;
606
609
  this.id = id;
607
610
  // this.customId = customId as TCustomId
608
611
  this.fullPath = fullPath;
@@ -624,6 +627,9 @@
624
627
  constructor(options) {
625
628
  super(options);
626
629
  }
630
+ static withRouterContext = () => {
631
+ return options => new RootRoute(options);
632
+ };
627
633
  }
628
634
 
629
635
  // const rootRoute = new RootRoute({
@@ -688,8 +694,13 @@
688
694
  routeSearch: {},
689
695
  search: {},
690
696
  status: 'idle'
697
+ }, {
698
+ onUpdate: next => {
699
+ this.state = next;
700
+ }
691
701
  })
692
702
  });
703
+ this.state = this.store.state;
693
704
  if (!this.#hasLoaders()) {
694
705
  this.store.setState(s => ({
695
706
  ...s,
@@ -697,12 +708,66 @@
697
708
  }));
698
709
  }
699
710
  }
711
+ #hasLoaders = () => {
712
+ return !!(this.route.options.onLoad || componentTypes.some(d => this.route.options[d]?.preload));
713
+ };
714
+ __init = opts => {
715
+ // Validate the search params and stabilize them
716
+ this.parentMatch = opts.parentMatch;
717
+ const parentSearch = this.parentMatch?.state.search ?? this.router.state.latestLocation.search;
718
+ try {
719
+ const validator = typeof this.route.options.validateSearch === 'object' ? this.route.options.validateSearch.parse : this.route.options.validateSearch;
720
+ let nextSearch = validator?.(parentSearch) ?? {};
721
+ this.store.setState(s => ({
722
+ ...s,
723
+ routeSearch: nextSearch,
724
+ search: {
725
+ ...parentSearch,
726
+ ...nextSearch
727
+ }
728
+ }));
729
+ componentTypes.map(async type => {
730
+ const component = this.route.options[type];
731
+ if (typeof this[type] !== 'function') {
732
+ this[type] = component;
733
+ }
734
+ });
735
+ const parent = this.parentMatch;
736
+ this.routeContext = this.route.options.getContext?.({
737
+ parentContext: parent?.routeContext,
738
+ context: parent?.context,
739
+ params: this.params,
740
+ search: this.state.search
741
+ }) || {};
742
+ this.context = parent ? {
743
+ ...parent.context,
744
+ ...this.routeContext
745
+ } : {
746
+ ...this.router?.options.context,
747
+ ...this.routeContext
748
+ };
749
+ } catch (err) {
750
+ console.error(err);
751
+ const error = new Error('Invalid search params found', {
752
+ cause: err
753
+ });
754
+ error.code = 'INVALID_SEARCH_PARAMS';
755
+ this.store.setState(s => ({
756
+ ...s,
757
+ status: 'error',
758
+ error: error
759
+ }));
760
+
761
+ // Do not proceed with loading the route
762
+ return;
763
+ }
764
+ };
700
765
  cancel = () => {
701
766
  this.abortController?.abort();
702
767
  };
703
768
  load = async opts => {
704
769
  // If the match is invalid, errored or idle, trigger it to load
705
- if (this.store.state.status !== 'pending') {
770
+ if (this.state.status !== 'pending') {
706
771
  await this.fetch(opts);
707
772
  }
708
773
  };
@@ -719,7 +784,7 @@
719
784
  // If the match was in an error state, set it
720
785
  // to a loading state again. Otherwise, keep it
721
786
  // as loading or resolved
722
- if (this.store.state.status === 'idle') {
787
+ if (this.state.status === 'idle') {
723
788
  this.store.setState(s => ({
724
789
  ...s,
725
790
  status: 'pending'
@@ -741,9 +806,11 @@
741
806
  if (this.route.options.onLoad) {
742
807
  return this.route.options.onLoad({
743
808
  params: this.params,
744
- search: this.store.state.search,
809
+ search: this.state.search,
745
810
  signal: this.abortController.signal,
746
- preload: !!opts?.preload
811
+ preload: !!opts?.preload,
812
+ routeContext: this.routeContext,
813
+ context: this.context
747
814
  });
748
815
  }
749
816
  return;
@@ -771,50 +838,6 @@
771
838
  });
772
839
  return this.__loadPromise;
773
840
  };
774
- #hasLoaders = () => {
775
- return !!(this.route.options.onLoad || componentTypes.some(d => this.route.options[d]?.preload));
776
- };
777
- __setParentMatch = parentMatch => {
778
- if (!this.parentMatch && parentMatch) {
779
- this.parentMatch = parentMatch;
780
- }
781
- };
782
- __validate = () => {
783
- // Validate the search params and stabilize them
784
- const parentSearch = this.parentMatch?.store.state.search ?? this.router.store.state.latestLocation.search;
785
- try {
786
- const validator = typeof this.route.options.validateSearch === 'object' ? this.route.options.validateSearch.parse : this.route.options.validateSearch;
787
- let nextSearch = validator?.(parentSearch) ?? {};
788
- this.store.setState(s => ({
789
- ...s,
790
- routeSearch: nextSearch,
791
- search: {
792
- ...parentSearch,
793
- ...nextSearch
794
- }
795
- }));
796
- componentTypes.map(async type => {
797
- const component = this.route.options[type];
798
- if (typeof this[type] !== 'function') {
799
- this[type] = component;
800
- }
801
- });
802
- } catch (err) {
803
- console.error(err);
804
- const error = new Error('Invalid search params found', {
805
- cause: err
806
- });
807
- error.code = 'INVALID_SEARCH_PARAMS';
808
- this.store.setState(s => ({
809
- ...s,
810
- status: 'error',
811
- error: error
812
- }));
813
-
814
- // Do not proceed with loading the route
815
- return;
816
- }
817
- };
818
841
  }
819
842
 
820
843
  const defaultParseSearch = parseSearchWith(JSON.parse);
@@ -899,7 +922,12 @@
899
922
  parseSearch: options?.parseSearch ?? defaultParseSearch,
900
923
  fetchServerDataFn: options?.fetchServerDataFn ?? defaultFetchServerDataFn
901
924
  };
902
- this.store = new Store(getInitialRouterState());
925
+ this.store = new Store(getInitialRouterState(), {
926
+ onUpdate: state => {
927
+ this.state = state;
928
+ }
929
+ });
930
+ this.state = this.store.state;
903
931
  this.basepath = '';
904
932
  this.update(options);
905
933
 
@@ -913,7 +941,7 @@
913
941
  // Mount only does anything on the client
914
942
  if (!isServer) {
915
943
  // If the router matches are empty, load the matches
916
- if (!this.store.state.currentMatches.length) {
944
+ if (!this.state.currentMatches.length) {
917
945
  this.load();
918
946
  }
919
947
  const visibilityChangeEvent = 'visibilitychange';
@@ -952,7 +980,9 @@
952
980
  currentLocation: parsedLocation
953
981
  }));
954
982
  this.#unsubHistory = this.history.listen(() => {
955
- this.load(this.#parseLocation(this.store.state.latestLocation));
983
+ this.load({
984
+ next: this.#parseLocation(this.state.latestLocation)
985
+ });
956
986
  });
957
987
  }
958
988
  const {
@@ -978,11 +1008,11 @@
978
1008
  });
979
1009
  };
980
1010
  cancelMatches = () => {
981
- [...this.store.state.currentMatches, ...(this.store.state.pendingMatches || [])].forEach(match => {
1011
+ [...this.state.currentMatches, ...(this.state.pendingMatches || [])].forEach(match => {
982
1012
  match.cancel();
983
1013
  });
984
1014
  };
985
- load = async next => {
1015
+ load = async opts => {
986
1016
  let now = Date.now();
987
1017
  const startedAt = now;
988
1018
  this.startedLoadingAt = startedAt;
@@ -991,29 +1021,31 @@
991
1021
  this.cancelMatches();
992
1022
  let matches;
993
1023
  this.store.batch(() => {
994
- if (next) {
1024
+ if (opts?.next) {
995
1025
  // Ingest the new location
996
1026
  this.store.setState(s => ({
997
1027
  ...s,
998
- latestLocation: next
1028
+ latestLocation: opts.next
999
1029
  }));
1000
1030
  }
1001
1031
 
1002
1032
  // Match the routes
1003
- matches = this.matchRoutes(this.store.state.latestLocation.pathname, {
1033
+ matches = this.matchRoutes(this.state.latestLocation.pathname, {
1004
1034
  strictParseParams: true
1005
1035
  });
1006
1036
  this.store.setState(s => ({
1007
1037
  ...s,
1008
1038
  status: 'pending',
1009
1039
  pendingMatches: matches,
1010
- pendingLocation: this.store.state.latestLocation
1040
+ pendingLocation: this.state.latestLocation
1011
1041
  }));
1012
1042
  });
1013
1043
 
1014
1044
  // Load the matches
1015
1045
  try {
1016
- await this.loadMatches(matches);
1046
+ await this.loadMatches(matches
1047
+ // opts
1048
+ );
1017
1049
  } catch (err) {
1018
1050
  console.warn(err);
1019
1051
  invariant(false, 'Matches failed to load due to error above ☝️. Navigation cancelled!');
@@ -1022,7 +1054,7 @@
1022
1054
  // Ignore side-effects of outdated side-effects
1023
1055
  return this.navigationPromise;
1024
1056
  }
1025
- const previousMatches = this.store.state.currentMatches;
1057
+ const previousMatches = this.state.currentMatches;
1026
1058
  const exiting = [],
1027
1059
  staying = [];
1028
1060
  previousMatches.forEach(d => {
@@ -1039,11 +1071,11 @@
1039
1071
  exiting.forEach(d => {
1040
1072
  d.__onExit?.({
1041
1073
  params: d.params,
1042
- search: d.store.state.routeSearch
1074
+ search: d.state.routeSearch
1043
1075
  });
1044
1076
 
1045
1077
  // Clear non-loading error states when match leaves
1046
- if (d.store.state.status === 'error') {
1078
+ if (d.state.status === 'error') {
1047
1079
  this.store.setState(s => ({
1048
1080
  ...s,
1049
1081
  status: 'idle',
@@ -1054,21 +1086,19 @@
1054
1086
  staying.forEach(d => {
1055
1087
  d.route.options.onTransition?.({
1056
1088
  params: d.params,
1057
- search: d.store.state.routeSearch
1089
+ search: d.state.routeSearch
1058
1090
  });
1059
1091
  });
1060
1092
  entering.forEach(d => {
1061
1093
  d.__onExit = d.route.options.onLoaded?.({
1062
1094
  params: d.params,
1063
- search: d.store.state.search
1095
+ search: d.state.search
1064
1096
  });
1065
- // delete this.store.state.matchCache[d.id] // TODO:
1066
1097
  });
1067
-
1068
1098
  this.store.setState(s => ({
1069
1099
  ...s,
1070
1100
  status: 'idle',
1071
- currentLocation: this.store.state.latestLocation,
1101
+ currentLocation: this.state.latestLocation,
1072
1102
  currentMatches: matches,
1073
1103
  pendingLocation: undefined,
1074
1104
  pendingMatches: undefined
@@ -1081,7 +1111,7 @@
1081
1111
  invariant(route, `Route with id "${id}" not found`);
1082
1112
  return route;
1083
1113
  };
1084
- loadRoute = async (navigateOpts = this.store.state.latestLocation) => {
1114
+ loadRoute = async (navigateOpts = this.state.latestLocation) => {
1085
1115
  const next = this.buildNext(navigateOpts);
1086
1116
  const matches = this.matchRoutes(next.pathname, {
1087
1117
  strictParseParams: true
@@ -1089,7 +1119,7 @@
1089
1119
  await this.loadMatches(matches);
1090
1120
  return matches;
1091
1121
  };
1092
- preloadRoute = async (navigateOpts = this.store.state.latestLocation) => {
1122
+ preloadRoute = async (navigateOpts = this.state.latestLocation) => {
1093
1123
  const next = this.buildNext(navigateOpts);
1094
1124
  const matches = this.matchRoutes(next.pathname, {
1095
1125
  strictParseParams: true
@@ -1104,7 +1134,7 @@
1104
1134
  if (!this.routeTree) {
1105
1135
  return matches;
1106
1136
  }
1107
- const existingMatches = [...this.store.state.currentMatches, ...(this.store.state.pendingMatches ?? [])];
1137
+ const existingMatches = [...this.state.currentMatches, ...(this.state.pendingMatches ?? [])];
1108
1138
  const findInRouteTree = async routes => {
1109
1139
  const parentMatch = last(matches);
1110
1140
  let params = parentMatch?.params ?? {};
@@ -1116,7 +1146,7 @@
1116
1146
  if (!route.path && children?.length) {
1117
1147
  return findMatchInRoutes([...matchingRoutes, route], children);
1118
1148
  }
1119
- const fuzzy = route.path === '/' ? false : !!children?.length;
1149
+ const fuzzy = !!(route.path !== '/' || children?.length);
1120
1150
  const matchParams = matchPathname(this.basepath, pathname, {
1121
1151
  to: route.fullPath,
1122
1152
  fuzzy,
@@ -1150,9 +1180,7 @@
1150
1180
  matchingRoutes.forEach(foundRoute => {
1151
1181
  const interpolatedPath = interpolatePath(foundRoute.path, params);
1152
1182
  const matchId = interpolatePath(foundRoute.id, params, true);
1153
- const match = existingMatches.find(d => d.id === matchId) ||
1154
- // this.store.state.matchCache[matchId]?.match || // TODO:
1155
- new RouteMatch(this, foundRoute, {
1183
+ const match = existingMatches.find(d => d.id === matchId) || new RouteMatch(this, foundRoute, {
1156
1184
  id: matchId,
1157
1185
  params,
1158
1186
  pathname: joinPaths([this.basepath, interpolatedPath])
@@ -1166,15 +1194,10 @@
1166
1194
  }
1167
1195
  };
1168
1196
  findInRouteTree([this.routeTree]);
1169
- linkMatches(matches);
1170
1197
  return matches;
1171
1198
  };
1172
- loadMatches = async (resolvedMatches, loaderOpts) => {
1173
- // this.cleanMatchCache()
1174
- resolvedMatches.forEach(async match => {
1175
- // Validate the match (loads search params etc)
1176
- match.__validate();
1177
- });
1199
+ loadMatches = async (resolvedMatches, opts) => {
1200
+ initMatches(resolvedMatches);
1178
1201
 
1179
1202
  // Check each match middleware to see if the route can be accessed
1180
1203
  await Promise.all(resolvedMatches.map(async match => {
@@ -1184,7 +1207,7 @@
1184
1207
  match
1185
1208
  });
1186
1209
  } catch (err) {
1187
- if (!loaderOpts?.preload) {
1210
+ if (!opts?.preload) {
1188
1211
  match.route.options.onLoadError?.(err);
1189
1212
  }
1190
1213
  throw err;
@@ -1192,14 +1215,16 @@
1192
1215
  }));
1193
1216
  const matchPromises = resolvedMatches.map(async (match, index) => {
1194
1217
  const prevMatch = resolvedMatches[1];
1195
- const search = match.store.state.search;
1196
- if (search.__data?.matchId && search.__data.matchId !== match.id) {
1197
- return;
1198
- }
1218
+ match.state.search;
1219
+
1220
+ // if (opts?.filter && !opts.filter(match)) {
1221
+ // return
1222
+ // }
1223
+
1199
1224
  match.load({
1200
- preload: loaderOpts?.preload
1225
+ preload: opts?.preload
1201
1226
  });
1202
- if (match.store.state.status !== 'success' && match.__loadPromise) {
1227
+ if (match.state.status !== 'success' && match.__loadPromise) {
1203
1228
  // Wait for the first sign of activity from the match
1204
1229
  await match.__loadPromise;
1205
1230
  }
@@ -1256,15 +1281,15 @@
1256
1281
  };
1257
1282
  const next = this.buildNext(location);
1258
1283
  if (opts?.pending) {
1259
- if (!this.store.state.pendingLocation) {
1284
+ if (!this.state.pendingLocation) {
1260
1285
  return false;
1261
1286
  }
1262
- return matchPathname(this.basepath, this.store.state.pendingLocation.pathname, {
1287
+ return matchPathname(this.basepath, this.state.pendingLocation.pathname, {
1263
1288
  ...opts,
1264
1289
  to: next.pathname
1265
1290
  });
1266
1291
  }
1267
- return matchPathname(this.basepath, this.store.state.currentLocation.pathname, {
1292
+ return matchPathname(this.basepath, this.state.currentLocation.pathname, {
1268
1293
  ...opts,
1269
1294
  to: next.pathname
1270
1295
  });
@@ -1310,11 +1335,11 @@
1310
1335
  const preloadDelay = userPreloadDelay ?? this.options.defaultPreloadDelay ?? 0;
1311
1336
 
1312
1337
  // Compare path/hash for matches
1313
- const pathIsEqual = this.store.state.currentLocation.pathname === next.pathname;
1314
- const currentPathSplit = this.store.state.currentLocation.pathname.split('/');
1338
+ const pathIsEqual = this.state.currentLocation.pathname === next.pathname;
1339
+ const currentPathSplit = this.state.currentLocation.pathname.split('/');
1315
1340
  const nextPathSplit = next.pathname.split('/');
1316
1341
  const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
1317
- const hashIsEqual = this.store.state.currentLocation.hash === next.hash;
1342
+ const hashIsEqual = this.state.currentLocation.hash === next.hash;
1318
1343
  // Combine the matches based on user options
1319
1344
  const pathTest = activeOptions?.exact ? pathIsEqual : pathIsFuzzyEqual;
1320
1345
  const hashTest = activeOptions?.includeHash ? hashIsEqual : true;
@@ -1377,21 +1402,18 @@
1377
1402
  dehydrate = () => {
1378
1403
  return {
1379
1404
  state: {
1380
- ...pick(this.store.state, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']),
1381
- currentMatches: this.store.state.currentMatches.map(match => ({
1405
+ ...pick(this.state, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']),
1406
+ currentMatches: this.state.currentMatches.map(match => ({
1382
1407
  id: match.id,
1383
1408
  state: {
1384
- ...pick(match.store.state, ['status'])
1409
+ status: match.state.status
1385
1410
  }
1386
1411
  }))
1387
- },
1388
- context: this.options.context
1412
+ }
1389
1413
  };
1390
1414
  };
1391
1415
  hydrate = dehydratedRouter => {
1392
1416
  this.store.setState(s => {
1393
- this.options.context = dehydratedRouter.context;
1394
-
1395
1417
  // Match the routes
1396
1418
  const currentMatches = this.matchRoutes(dehydratedRouter.state.latestLocation.pathname, {
1397
1419
  strictParseParams: true
@@ -1404,7 +1426,7 @@
1404
1426
  ...dehydratedMatch.state
1405
1427
  }));
1406
1428
  });
1407
- currentMatches.forEach(match => match.__validate());
1429
+ initMatches(currentMatches);
1408
1430
  return {
1409
1431
  ...s,
1410
1432
  ...dehydratedRouter.state,
@@ -1415,9 +1437,10 @@
1415
1437
  #buildRouteTree = routeTree => {
1416
1438
  const recurseRoutes = routes => {
1417
1439
  routes.forEach((route, i) => {
1418
- route.init();
1419
- route.originalIndex = i;
1420
- route.router = this;
1440
+ route.init({
1441
+ originalIndex: i,
1442
+ router: this
1443
+ });
1421
1444
  const existingRoute = this.routesById[route.id];
1422
1445
  if (existingRoute) {
1423
1446
  {
@@ -1455,9 +1478,9 @@
1455
1478
  this.load();
1456
1479
  };
1457
1480
  #buildLocation = (dest = {}) => {
1458
- const fromPathname = dest.fromCurrent ? this.store.state.latestLocation.pathname : dest.from ?? this.store.state.latestLocation.pathname;
1481
+ const fromPathname = dest.fromCurrent ? this.state.latestLocation.pathname : dest.from ?? this.state.latestLocation.pathname;
1459
1482
  let pathname = resolvePath(this.basepath ?? '/', fromPathname, `${dest.to ?? '.'}`);
1460
- const fromMatches = this.matchRoutes(this.store.state.latestLocation.pathname, {
1483
+ const fromMatches = this.matchRoutes(this.state.latestLocation.pathname, {
1461
1484
  strictParseParams: true
1462
1485
  });
1463
1486
  const toMatches = this.matchRoutes(pathname);
@@ -1473,7 +1496,7 @@
1473
1496
  pathname = interpolatePath(pathname, nextParams ?? {});
1474
1497
 
1475
1498
  // Pre filters first
1476
- const preFilteredSearch = dest.__preSearchFilters?.length ? dest.__preSearchFilters?.reduce((prev, next) => next(prev), this.store.state.latestLocation.search) : this.store.state.latestLocation.search;
1499
+ const preFilteredSearch = dest.__preSearchFilters?.length ? dest.__preSearchFilters?.reduce((prev, next) => next(prev), this.state.latestLocation.search) : this.state.latestLocation.search;
1477
1500
 
1478
1501
  // Then the link/navigate function
1479
1502
  const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
@@ -1483,15 +1506,15 @@
1483
1506
 
1484
1507
  // Then post filters
1485
1508
  const postFilteredSearch = dest.__postSearchFilters?.length ? dest.__postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
1486
- const search = replaceEqualDeep(this.store.state.latestLocation.search, postFilteredSearch);
1509
+ const search = replaceEqualDeep(this.state.latestLocation.search, postFilteredSearch);
1487
1510
  const searchStr = this.options.stringifySearch(search);
1488
- let hash = dest.hash === true ? this.store.state.latestLocation.hash : functionalUpdate(dest.hash, this.store.state.latestLocation.hash);
1511
+ let hash = dest.hash === true ? this.state.latestLocation.hash : functionalUpdate(dest.hash, this.state.latestLocation.hash);
1489
1512
  hash = hash ? `#${hash}` : '';
1490
1513
  return {
1491
1514
  pathname,
1492
1515
  search,
1493
1516
  searchStr,
1494
- state: this.store.state.latestLocation.state,
1517
+ state: this.state.latestLocation.state,
1495
1518
  hash,
1496
1519
  href: `${pathname}${searchStr}${hash}`,
1497
1520
  key: dest.key
@@ -1505,7 +1528,7 @@
1505
1528
  if (!location.replace) {
1506
1529
  nextAction = 'push';
1507
1530
  }
1508
- const isSameUrl = this.store.state.latestLocation.href === next.href;
1531
+ const isSameUrl = this.state.latestLocation.href === next.href;
1509
1532
  if (isSameUrl && !next.key) {
1510
1533
  nextAction = 'replace';
1511
1534
  }
@@ -1515,7 +1538,7 @@
1515
1538
  ...next.state
1516
1539
  });
1517
1540
 
1518
- // this.load(this.#parseLocation(this.store.state.latestLocation))
1541
+ // this.load(this.#parseLocation(this.state.latestLocation))
1519
1542
 
1520
1543
  return this.navigationPromise = new Promise(resolve => {
1521
1544
  const previousNavigationResolve = this.resolveNavigation;
@@ -1536,32 +1559,17 @@
1536
1559
  currentLocation: null,
1537
1560
  currentMatches: [],
1538
1561
  lastUpdated: Date.now()
1539
- // matchCache: {}, // TODO:
1540
- // get isFetching() {
1541
- // return (
1542
- // this.status === 'loading' ||
1543
- // this.currentMatches.some((d) => d.store.state.isFetching)
1544
- // )
1545
- // },
1546
- // get isPreloading() {
1547
- // return Object.values(this.matchCache).some(
1548
- // (d) =>
1549
- // d.match.store.state.isFetching &&
1550
- // !this.currentMatches.find((dd) => dd.id === d.match.id),
1551
- // )
1552
- // },
1553
1562
  };
1554
1563
  }
1555
-
1556
1564
  function isCtrlEvent(e) {
1557
1565
  return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
1558
1566
  }
1559
- function linkMatches(matches) {
1567
+ function initMatches(matches) {
1560
1568
  matches.forEach((match, index) => {
1561
- const parent = matches[index - 1];
1562
- if (parent) {
1563
- match.__setParentMatch(parent);
1564
- }
1569
+ const parentMatch = matches[index - 1];
1570
+ match.__init({
1571
+ parentMatch
1572
+ });
1565
1573
  });
1566
1574
  }
1567
1575
 
@@ -1637,7 +1645,7 @@
1637
1645
  //
1638
1646
 
1639
1647
  function useLinkProps(options) {
1640
- const router = useRouter();
1648
+ const router = useRouterContext();
1641
1649
  const {
1642
1650
  // custom props
1643
1651
  type,
@@ -1762,7 +1770,7 @@
1762
1770
  ...rest
1763
1771
  }) {
1764
1772
  router.update(rest);
1765
- const currentMatches = useStore(router.store, s => s.currentMatches, undefined);
1773
+ const currentMatches = useStore(router.store, s => s.currentMatches);
1766
1774
  React__namespace.useEffect(router.mount, [router]);
1767
1775
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(routerContext.Provider, {
1768
1776
  value: {
@@ -1772,22 +1780,24 @@
1772
1780
  value: [undefined, ...currentMatches]
1773
1781
  }, /*#__PURE__*/React__namespace.createElement(Outlet, null))));
1774
1782
  }
1775
- function useRouter() {
1783
+ function useRouterContext() {
1776
1784
  const value = React__namespace.useContext(routerContext);
1777
1785
  warning(!value, 'useRouter must be used inside a <Router> component!');
1786
+ useStore(value.router.store);
1778
1787
  return value.router;
1779
1788
  }
1780
- function useRouterStore(selector, shallow) {
1781
- const router = useRouter();
1782
- return useStore(router.store, selector, shallow);
1789
+ function useRouter(track, shallow) {
1790
+ const router = useRouterContext();
1791
+ useStore(router.store, track, shallow);
1792
+ return router;
1783
1793
  }
1784
1794
  function useMatches() {
1785
1795
  return React__namespace.useContext(matchesContext);
1786
1796
  }
1787
1797
  function useMatch(opts) {
1788
- const router = useRouter();
1798
+ const router = useRouterContext();
1789
1799
  const nearestMatch = useMatches()[0];
1790
- const match = opts?.from ? router.store.state.currentMatches.find(d => d.route.id === opts?.from) : nearestMatch;
1800
+ const match = opts?.from ? router.state.currentMatches.find(d => d.route.id === opts?.from) : nearestMatch;
1791
1801
  invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
1792
1802
  if (opts?.strict ?? true) {
1793
1803
  invariant(nearestMatch.route.id == match?.route.id, `useMatch("${match?.route.id}") is being called in a component that is meant to render the '${nearestMatch.route.id}' route. Did you mean to 'useMatch("${match?.route.id}", { strict: false })' or 'useRoute("${match?.route.id}")' instead?`);
@@ -1796,26 +1806,26 @@
1796
1806
  return match;
1797
1807
  }
1798
1808
  function useRoute(routeId) {
1799
- const router = useRouter();
1809
+ const router = useRouterContext();
1800
1810
  const resolvedRoute = router.getRoute(routeId);
1801
1811
  invariant(resolvedRoute, `Could not find a route for route "${routeId}"! Did you forget to add it to your route config?`);
1802
1812
  return resolvedRoute;
1803
1813
  }
1804
1814
  function useSearch(opts) {
1805
1815
  const match = useMatch(opts);
1806
- useStore(match.store, d => opts?.track?.(d.search) ?? d.search);
1807
- return match.store.state.search;
1816
+ useStore(match.store, d => opts?.track?.(d.search) ?? d.search, true);
1817
+ return match.state.search;
1808
1818
  }
1809
1819
  function useParams(opts) {
1810
- const router = useRouter();
1820
+ const router = useRouterContext();
1811
1821
  useStore(router.store, d => {
1812
1822
  const params = last(d.currentMatches)?.params;
1813
1823
  return opts?.track?.(params) ?? params;
1814
- });
1815
- return last(router.store.state.currentMatches)?.params;
1824
+ }, true);
1825
+ return last(router.state.currentMatches)?.params;
1816
1826
  }
1817
1827
  function useNavigate(defaultOpts) {
1818
- const router = useRouter();
1828
+ const router = useRouterContext();
1819
1829
  return opts => {
1820
1830
  return router.navigate({
1821
1831
  ...defaultOpts,
@@ -1824,7 +1834,7 @@
1824
1834
  };
1825
1835
  }
1826
1836
  function useMatchRoute() {
1827
- const router = useRouter();
1837
+ const router = useRouterContext();
1828
1838
  return opts => {
1829
1839
  const {
1830
1840
  pending,
@@ -1863,17 +1873,17 @@
1863
1873
  matches,
1864
1874
  match
1865
1875
  }) {
1866
- const router = useRouter();
1867
- useStore(match.store);
1876
+ const router = useRouterContext();
1877
+ useStore(match.store, store => [store.status, store.error], true);
1868
1878
  const defaultPending = React__namespace.useCallback(() => null, []);
1869
1879
  const Inner = React__namespace.useCallback(props => {
1870
- if (props.match.store.state.status === 'error') {
1871
- throw props.match.store.state.error;
1880
+ if (props.match.state.status === 'error') {
1881
+ throw props.match.state.error;
1872
1882
  }
1873
- if (props.match.store.state.status === 'success') {
1883
+ if (props.match.state.status === 'success') {
1874
1884
  return /*#__PURE__*/React__namespace.createElement(props.match.component ?? router.options.defaultComponent ?? Outlet);
1875
1885
  }
1876
- if (props.match.store.state.status === 'pending') {
1886
+ if (props.match.state.status === 'pending') {
1877
1887
  throw props.match.__loadPromise;
1878
1888
  }
1879
1889
  invariant(false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
@@ -1882,7 +1892,7 @@
1882
1892
  const errorComponent = match.errorComponent ?? router.options.defaultErrorComponent;
1883
1893
  return /*#__PURE__*/React__namespace.createElement(matchesContext.Provider, {
1884
1894
  value: matches
1885
- }, /*#__PURE__*/React__namespace.createElement(React__namespace.Suspense, {
1895
+ }, match.route.options.wrapInSuspense ?? true ? /*#__PURE__*/React__namespace.createElement(React__namespace.Suspense, {
1886
1896
  fallback: /*#__PURE__*/React__namespace.createElement(PendingComponent, null)
1887
1897
  }, /*#__PURE__*/React__namespace.createElement(CatchBoundary, {
1888
1898
  key: match.route.id,
@@ -1890,7 +1900,13 @@
1890
1900
  match: match
1891
1901
  }, /*#__PURE__*/React__namespace.createElement(Inner, {
1892
1902
  match: match
1893
- }))));
1903
+ }))) : /*#__PURE__*/React__namespace.createElement(CatchBoundary, {
1904
+ key: match.route.id,
1905
+ errorComponent: errorComponent,
1906
+ match: match
1907
+ }, /*#__PURE__*/React__namespace.createElement(Inner, {
1908
+ match: match
1909
+ })));
1894
1910
  }
1895
1911
 
1896
1912
  // This is the messiest thing ever... I'm either seriously tired (likely) or
@@ -1919,17 +1935,17 @@
1919
1935
  }
1920
1936
  function CatchBoundaryInner(props) {
1921
1937
  const [activeErrorState, setActiveErrorState] = React__namespace.useState(props.errorState);
1922
- const router = useRouter();
1938
+ const router = useRouterContext();
1923
1939
  const errorComponent = props.errorComponent ?? DefaultErrorBoundary;
1924
1940
  const prevKeyRef = React__namespace.useRef('');
1925
1941
  React__namespace.useEffect(() => {
1926
1942
  if (activeErrorState) {
1927
- if (router.store.state.currentLocation.key !== prevKeyRef.current) {
1943
+ if (router.state.currentLocation.key !== prevKeyRef.current) {
1928
1944
  setActiveErrorState({});
1929
1945
  }
1930
1946
  }
1931
- prevKeyRef.current = router.store.state.currentLocation.key;
1932
- }, [activeErrorState, router.store.state.currentLocation.key]);
1947
+ prevKeyRef.current = router.state.currentLocation.key;
1948
+ }, [activeErrorState, router.state.currentLocation.key]);
1933
1949
  React__namespace.useEffect(() => {
1934
1950
  if (props.errorState.error) {
1935
1951
  setActiveErrorState(props.errorState);
@@ -2043,7 +2059,7 @@
2043
2059
  exports.useParams = useParams;
2044
2060
  exports.useRoute = useRoute;
2045
2061
  exports.useRouter = useRouter;
2046
- exports.useRouterStore = useRouterStore;
2062
+ exports.useRouterContext = useRouterContext;
2047
2063
  exports.useSearch = useSearch;
2048
2064
  exports.useStore = useStore;
2049
2065
  exports.warning = warning;