@tanstack/react-router 0.0.1-beta.235 → 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.
Files changed (39) hide show
  1. package/build/cjs/Matches.js +34 -19
  2. package/build/cjs/Matches.js.map +1 -1
  3. package/build/cjs/RouterProvider.js +31 -26
  4. package/build/cjs/RouterProvider.js.map +1 -1
  5. package/build/cjs/index.js +1 -0
  6. package/build/cjs/index.js.map +1 -1
  7. package/build/cjs/route.js +13 -7
  8. package/build/cjs/route.js.map +1 -1
  9. package/build/cjs/router.js +49 -37
  10. package/build/cjs/router.js.map +1 -1
  11. package/build/cjs/useParams.js +7 -2
  12. package/build/cjs/useParams.js.map +1 -1
  13. package/build/cjs/useSearch.js +6 -1
  14. package/build/cjs/useSearch.js.map +1 -1
  15. package/build/cjs/utils.js +4 -1
  16. package/build/cjs/utils.js.map +1 -1
  17. package/build/esm/index.js +144 -94
  18. package/build/esm/index.js.map +1 -1
  19. package/build/stats-html.html +1 -1
  20. package/build/stats-react.json +574 -293
  21. package/build/types/Matches.d.ts +9 -3
  22. package/build/types/RouterProvider.d.ts +3 -0
  23. package/build/types/route.d.ts +30 -10
  24. package/build/types/router.d.ts +6 -3
  25. package/build/types/useParams.d.ts +3 -1
  26. package/build/types/useSearch.d.ts +3 -1
  27. package/build/types/utils.d.ts +3 -1
  28. package/build/umd/index.development.js +412 -97
  29. package/build/umd/index.development.js.map +1 -1
  30. package/build/umd/index.production.js +2 -2
  31. package/build/umd/index.production.js.map +1 -1
  32. package/package.json +4 -2
  33. package/src/Matches.tsx +72 -37
  34. package/src/RouterProvider.tsx +42 -31
  35. package/src/route.ts +37 -15
  36. package/src/router.ts +62 -44
  37. package/src/useParams.tsx +14 -4
  38. package/src/useSearch.tsx +11 -3
  39. package/src/utils.ts +20 -12
@@ -9,10 +9,10 @@
9
9
  * @license MIT
10
10
  */
11
11
  (function (global, factory) {
12
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) :
13
- typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) :
14
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ReactRouter = {}, global.React));
15
- })(this, (function (exports, React) { 'use strict';
12
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('use-sync-external-store/shim')) :
13
+ typeof define === 'function' && define.amd ? define(['exports', 'react', 'use-sync-external-store/shim'], factory) :
14
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ReactRouter = {}, global.React, global.require$$1));
15
+ })(this, (function (exports, React, require$$1) { 'use strict';
16
16
 
17
17
  function _interopNamespaceDefault(e) {
18
18
  var n = Object.create(null);
@@ -342,6 +342,272 @@
342
342
  }
343
343
  }
344
344
 
345
+ var withSelector = {exports: {}};
346
+
347
+ var withSelector_development = {};
348
+
349
+ /**
350
+ * @license React
351
+ * use-sync-external-store-shim/with-selector.development.js
352
+ *
353
+ * Copyright (c) Facebook, Inc. and its affiliates.
354
+ *
355
+ * This source code is licensed under the MIT license found in the
356
+ * LICENSE file in the root directory of this source tree.
357
+ */
358
+
359
+ var hasRequiredWithSelector_development;
360
+
361
+ function requireWithSelector_development () {
362
+ if (hasRequiredWithSelector_development) return withSelector_development;
363
+ hasRequiredWithSelector_development = 1;
364
+
365
+ {
366
+ (function() {
367
+
368
+ /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
369
+ if (
370
+ typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
371
+ typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart ===
372
+ 'function'
373
+ ) {
374
+ __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
375
+ }
376
+ var React$1 = React;
377
+ var shim = require$$1;
378
+
379
+ /**
380
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
381
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
382
+ */
383
+ function is(x, y) {
384
+ return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
385
+ ;
386
+ }
387
+
388
+ var objectIs = typeof Object.is === 'function' ? Object.is : is;
389
+
390
+ var useSyncExternalStore = shim.useSyncExternalStore;
391
+
392
+ // for CommonJS interop.
393
+
394
+ var useRef = React$1.useRef,
395
+ useEffect = React$1.useEffect,
396
+ useMemo = React$1.useMemo,
397
+ useDebugValue = React$1.useDebugValue; // Same as useSyncExternalStore, but supports selector and isEqual arguments.
398
+
399
+ function useSyncExternalStoreWithSelector(subscribe, getSnapshot, getServerSnapshot, selector, isEqual) {
400
+ // Use this to track the rendered snapshot.
401
+ var instRef = useRef(null);
402
+ var inst;
403
+
404
+ if (instRef.current === null) {
405
+ inst = {
406
+ hasValue: false,
407
+ value: null
408
+ };
409
+ instRef.current = inst;
410
+ } else {
411
+ inst = instRef.current;
412
+ }
413
+
414
+ var _useMemo = useMemo(function () {
415
+ // Track the memoized state using closure variables that are local to this
416
+ // memoized instance of a getSnapshot function. Intentionally not using a
417
+ // useRef hook, because that state would be shared across all concurrent
418
+ // copies of the hook/component.
419
+ var hasMemo = false;
420
+ var memoizedSnapshot;
421
+ var memoizedSelection;
422
+
423
+ var memoizedSelector = function (nextSnapshot) {
424
+ if (!hasMemo) {
425
+ // The first time the hook is called, there is no memoized result.
426
+ hasMemo = true;
427
+ memoizedSnapshot = nextSnapshot;
428
+
429
+ var _nextSelection = selector(nextSnapshot);
430
+
431
+ if (isEqual !== undefined) {
432
+ // Even if the selector has changed, the currently rendered selection
433
+ // may be equal to the new selection. We should attempt to reuse the
434
+ // current value if possible, to preserve downstream memoizations.
435
+ if (inst.hasValue) {
436
+ var currentSelection = inst.value;
437
+
438
+ if (isEqual(currentSelection, _nextSelection)) {
439
+ memoizedSelection = currentSelection;
440
+ return currentSelection;
441
+ }
442
+ }
443
+ }
444
+
445
+ memoizedSelection = _nextSelection;
446
+ return _nextSelection;
447
+ } // We may be able to reuse the previous invocation's result.
448
+
449
+
450
+ // We may be able to reuse the previous invocation's result.
451
+ var prevSnapshot = memoizedSnapshot;
452
+ var prevSelection = memoizedSelection;
453
+
454
+ if (objectIs(prevSnapshot, nextSnapshot)) {
455
+ // The snapshot is the same as last time. Reuse the previous selection.
456
+ return prevSelection;
457
+ } // The snapshot has changed, so we need to compute a new selection.
458
+
459
+
460
+ // The snapshot has changed, so we need to compute a new selection.
461
+ var nextSelection = selector(nextSnapshot); // If a custom isEqual function is provided, use that to check if the data
462
+ // has changed. If it hasn't, return the previous selection. That signals
463
+ // to React that the selections are conceptually equal, and we can bail
464
+ // out of rendering.
465
+
466
+ // If a custom isEqual function is provided, use that to check if the data
467
+ // has changed. If it hasn't, return the previous selection. That signals
468
+ // to React that the selections are conceptually equal, and we can bail
469
+ // out of rendering.
470
+ if (isEqual !== undefined && isEqual(prevSelection, nextSelection)) {
471
+ return prevSelection;
472
+ }
473
+
474
+ memoizedSnapshot = nextSnapshot;
475
+ memoizedSelection = nextSelection;
476
+ return nextSelection;
477
+ }; // Assigning this to a constant so that Flow knows it can't change.
478
+
479
+
480
+ // Assigning this to a constant so that Flow knows it can't change.
481
+ var maybeGetServerSnapshot = getServerSnapshot === undefined ? null : getServerSnapshot;
482
+
483
+ var getSnapshotWithSelector = function () {
484
+ return memoizedSelector(getSnapshot());
485
+ };
486
+
487
+ var getServerSnapshotWithSelector = maybeGetServerSnapshot === null ? undefined : function () {
488
+ return memoizedSelector(maybeGetServerSnapshot());
489
+ };
490
+ return [getSnapshotWithSelector, getServerSnapshotWithSelector];
491
+ }, [getSnapshot, getServerSnapshot, selector, isEqual]),
492
+ getSelection = _useMemo[0],
493
+ getServerSelection = _useMemo[1];
494
+
495
+ var value = useSyncExternalStore(subscribe, getSelection, getServerSelection);
496
+ useEffect(function () {
497
+ inst.hasValue = true;
498
+ inst.value = value;
499
+ }, [value]);
500
+ useDebugValue(value);
501
+ return value;
502
+ }
503
+
504
+ withSelector_development.useSyncExternalStoreWithSelector = useSyncExternalStoreWithSelector;
505
+ /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
506
+ if (
507
+ typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
508
+ typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop ===
509
+ 'function'
510
+ ) {
511
+ __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(new Error());
512
+ }
513
+
514
+ })();
515
+ }
516
+ return withSelector_development;
517
+ }
518
+
519
+ {
520
+ withSelector.exports = requireWithSelector_development();
521
+ }
522
+
523
+ var withSelectorExports = withSelector.exports;
524
+
525
+ // src/index.ts
526
+ var Store = class {
527
+ constructor(initialState, options) {
528
+ this.listeners = /* @__PURE__ */ new Set();
529
+ this._batching = false;
530
+ this._flushing = 0;
531
+ this._nextPriority = null;
532
+ this.subscribe = (listener) => {
533
+ this.listeners.add(listener);
534
+ const unsub = this.options?.onSubscribe?.(listener, this);
535
+ return () => {
536
+ this.listeners.delete(listener);
537
+ unsub?.();
538
+ };
539
+ };
540
+ this.setState = (updater, opts) => {
541
+ const previous = this.state;
542
+ this.state = this.options?.updateFn ? this.options.updateFn(previous)(updater) : updater(previous);
543
+ const priority = opts?.priority ?? this.options?.defaultPriority ?? "high";
544
+ if (this._nextPriority === null) {
545
+ this._nextPriority = priority;
546
+ } else if (this._nextPriority === "high") {
547
+ this._nextPriority = priority;
548
+ } else {
549
+ this._nextPriority = this.options?.defaultPriority ?? "high";
550
+ }
551
+ this.options?.onUpdate?.({
552
+ priority: this._nextPriority
553
+ });
554
+ this._flush();
555
+ };
556
+ this._flush = () => {
557
+ if (this._batching)
558
+ return;
559
+ const flushId = ++this._flushing;
560
+ this.listeners.forEach((listener) => {
561
+ if (this._flushing !== flushId)
562
+ return;
563
+ listener({
564
+ priority: this._nextPriority ?? "high"
565
+ });
566
+ });
567
+ };
568
+ this.batch = (cb) => {
569
+ if (this._batching)
570
+ return cb();
571
+ this._batching = true;
572
+ cb();
573
+ this._batching = false;
574
+ this._flush();
575
+ };
576
+ this.state = initialState;
577
+ this.options = options;
578
+ }
579
+ };
580
+
581
+ // src/index.ts
582
+ function useStore(store, selector = (d) => d) {
583
+ const slice = withSelectorExports.useSyncExternalStoreWithSelector(
584
+ store.subscribe,
585
+ () => store.state,
586
+ () => store.state,
587
+ selector,
588
+ shallow$1
589
+ );
590
+ return slice;
591
+ }
592
+ function shallow$1(objA, objB) {
593
+ if (Object.is(objA, objB)) {
594
+ return true;
595
+ }
596
+ if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) {
597
+ return false;
598
+ }
599
+ const keysA = Object.keys(objA);
600
+ if (keysA.length !== Object.keys(objB).length) {
601
+ return false;
602
+ }
603
+ for (let i = 0; i < keysA.length; i++) {
604
+ if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !Object.is(objA[keysA[i]], objB[keysA[i]])) {
605
+ return false;
606
+ }
607
+ }
608
+ return true;
609
+ }
610
+
345
611
  function CatchBoundary(props) {
346
612
  const errorComponent = props.errorComponent ?? ErrorComponent;
347
613
  return /*#__PURE__*/React__namespace.createElement(CatchBoundaryImpl, {
@@ -608,7 +874,10 @@
608
874
  return true;
609
875
  }
610
876
  function useRouteContext(opts) {
611
- return useMatch(opts).context;
877
+ return useMatch({
878
+ ...opts,
879
+ select: match => opts?.select ? opts.select(match.context) : match.context
880
+ });
612
881
  }
613
882
  const useLayoutEffect$1 = typeof window !== 'undefined' ? React__namespace.useLayoutEffect : React__namespace.useEffect;
614
883
  function escapeJSON(jsonString) {
@@ -801,12 +1070,22 @@
801
1070
  return isMatch ? params : undefined;
802
1071
  }
803
1072
 
804
- function useParams(_opts) {
805
- return last(useRouter().state.matches)?.params;
1073
+ function useParams(opts) {
1074
+ return useRouterState({
1075
+ select: state => {
1076
+ const params = last(state.matches)?.params;
1077
+ return opts?.select ? opts.select(params) : params;
1078
+ }
1079
+ });
806
1080
  }
807
1081
 
808
1082
  function useSearch(opts) {
809
- return useMatch(opts).search;
1083
+ return useMatch({
1084
+ ...opts,
1085
+ select: match => {
1086
+ return opts?.select ? opts.select(match.search) : match.search;
1087
+ }
1088
+ });
810
1089
  }
811
1090
 
812
1091
  const rootRouteId = '__root__';
@@ -871,28 +1150,34 @@
871
1150
  // This is a dummy static method that should get
872
1151
  // replaced by a framework specific implementation if necessary
873
1152
  };
874
- useMatch = () => {
1153
+ useMatch = opts => {
875
1154
  return useMatch({
1155
+ ...opts,
876
1156
  from: this.id
877
1157
  });
878
1158
  };
879
- useRouteContext = () => {
1159
+ useRouteContext = opts => {
880
1160
  return useMatch({
881
- from: this.id
882
- }).context;
1161
+ ...opts,
1162
+ from: this.id,
1163
+ select: d => opts?.select ? opts.select(d.context) : d.context
1164
+ });
883
1165
  };
884
- useSearch = () => {
1166
+ useSearch = opts => {
885
1167
  return useSearch({
1168
+ ...opts,
886
1169
  from: this.id
887
1170
  });
888
1171
  };
889
- useParams = () => {
1172
+ useParams = opts => {
890
1173
  return useParams({
1174
+ ...opts,
891
1175
  from: this.id
892
1176
  });
893
1177
  };
894
- useLoaderData = () => {
1178
+ useLoaderData = opts => {
895
1179
  return useLoaderData({
1180
+ ...opts,
896
1181
  from: this.id
897
1182
  });
898
1183
  };
@@ -914,12 +1199,13 @@
914
1199
  //
915
1200
 
916
1201
  function Matches() {
917
- const router = useRouter();
918
1202
  const {
919
- matches
920
- } = router.state;
921
- const locationKey = router.state.location.state.key;
922
- const route = router.routesById[rootRouteId];
1203
+ routesById
1204
+ } = useRouter();
1205
+ const routerState = useRouterState();
1206
+ const matches = routerState.pendingMatches?.some(d => d.showPending) ? routerState.pendingMatches : routerState.matches;
1207
+ const locationKey = useRouterState().location.state.key;
1208
+ const route = routesById[rootRouteId];
923
1209
  const errorComponent = React__namespace.useCallback(props => {
924
1210
  return /*#__PURE__*/React__namespace.createElement(ErrorComponent, {
925
1211
  ...props,
@@ -1059,31 +1345,45 @@
1059
1345
  return !!params ? props.children : null;
1060
1346
  }
1061
1347
  function useMatch(opts) {
1062
- const router = useRouter();
1063
1348
  const nearestMatch = React__namespace.useContext(matchesContext)[0];
1064
1349
  const nearestMatchRouteId = nearestMatch?.routeId;
1065
- const matchRouteId = (() => {
1066
- const match = opts?.from ? router.state.matches.find(d => d.routeId === opts?.from) : router.state.matches.find(d => d.id === nearestMatch.id);
1067
- return match.routeId;
1068
- })();
1350
+ const matchRouteId = useRouterState({
1351
+ select: state => {
1352
+ const matches = state.pendingMatches?.some(d => d.showPending) ? state.pendingMatches : state.matches;
1353
+ const match = opts?.from ? matches.find(d => d.routeId === opts?.from) : matches.find(d => d.id === nearestMatch.id);
1354
+ return match.routeId;
1355
+ }
1356
+ });
1069
1357
  if (opts?.strict ?? true) {
1070
1358
  invariant(nearestMatchRouteId == matchRouteId, `useMatch("${matchRouteId}") is being called in a component that is meant to render the '${nearestMatchRouteId}' route. Did you mean to 'useMatch("${matchRouteId}", { strict: false })' or 'useRoute("${matchRouteId}")' instead?`);
1071
1359
  }
1072
- const matchSelection = (() => {
1073
- const match = opts?.from ? router.state.matches.find(d => d.routeId === opts?.from) : router.state.matches.find(d => d.id === nearestMatch.id);
1074
- invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
1075
- return match;
1076
- })();
1360
+ const matchSelection = useRouterState({
1361
+ select: state => {
1362
+ const matches = state.pendingMatches?.some(d => d.showPending) ? state.pendingMatches : state.matches;
1363
+ const match = opts?.from ? matches.find(d => d.routeId === opts?.from) : matches.find(d => d.id === nearestMatch.id);
1364
+ invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
1365
+ return opts?.select ? opts.select(match) : match;
1366
+ }
1367
+ });
1077
1368
  return matchSelection;
1078
1369
  }
1079
1370
  const matchesContext = /*#__PURE__*/React__namespace.createContext(null);
1080
- function useMatches() {
1081
- const router = useRouter();
1371
+ function useMatches(opts) {
1082
1372
  const contextMatches = React__namespace.useContext(matchesContext);
1083
- return router.state.matches.slice(router.state.matches.findIndex(d => d.id === contextMatches[0]?.id));
1373
+ return useRouterState({
1374
+ select: state => {
1375
+ let matches = state.pendingMatches?.some(d => d.showPending) ? state.pendingMatches : state.matches;
1376
+ matches = matches.slice(matches.findIndex(d => d.id === contextMatches[0]?.id));
1377
+ return opts?.select ? opts.select(matches) : matches;
1378
+ }
1379
+ });
1084
1380
  }
1085
1381
  function useLoaderData(opts) {
1086
- return useMatch(opts)?.loaderData;
1382
+ const match = useMatch({
1383
+ ...opts,
1384
+ select: undefined
1385
+ });
1386
+ return typeof opts.select === 'function' ? opts.select(match?.loaderData) : match?.loaderData;
1087
1387
  }
1088
1388
 
1089
1389
  const routerContext = /*#__PURE__*/React__namespace.createContext(null);
@@ -1114,31 +1414,29 @@
1114
1414
  function RouterProviderInner({
1115
1415
  router
1116
1416
  }) {
1117
- const [preState, setState] = React__namespace.useState(() => router.state);
1118
1417
  const [isTransitioning, startReactTransition] = React__namespace.useTransition();
1119
- const isAnyTransitioning = isTransitioning || preState.matches.some(d => d.status === 'pending');
1120
- const state = React__namespace.useMemo(() => ({
1121
- ...preState,
1122
- status: isAnyTransitioning ? 'pending' : 'idle',
1123
- location: isTransitioning ? router.latestLocation : preState.location,
1124
- pendingMatches: router.pendingMatches
1125
- }), [preState, isTransitioning]);
1126
- router.setState = setState;
1127
- router.state = state;
1128
1418
  router.startReactTransition = startReactTransition;
1419
+ React__namespace.useEffect(() => {
1420
+ if (isTransitioning) {
1421
+ router.__store.setState(s => ({
1422
+ ...s,
1423
+ isTransitioning
1424
+ }));
1425
+ }
1426
+ }, [isTransitioning]);
1129
1427
  const tryLoad = () => {
1130
- startReactTransition(() => {
1131
- try {
1132
- router.load();
1133
- } catch (err) {
1134
- console.error(err);
1135
- }
1136
- });
1428
+ // startReactTransition(() => {
1429
+ try {
1430
+ router.load();
1431
+ } catch (err) {
1432
+ console.error(err);
1433
+ }
1434
+ // })
1137
1435
  };
1138
1436
  useLayoutEffect$1(() => {
1139
1437
  const unsub = router.history.subscribe(() => {
1140
1438
  router.latestLocation = router.parseLocation(router.latestLocation);
1141
- if (state.location !== router.latestLocation) {
1439
+ if (router.state.location !== router.latestLocation) {
1142
1440
  tryLoad();
1143
1441
  }
1144
1442
  });
@@ -1148,7 +1446,7 @@
1148
1446
  hash: true,
1149
1447
  state: true
1150
1448
  });
1151
- if (state.location.href !== nextLocation.href) {
1449
+ if (router.state.location.href !== nextLocation.href) {
1152
1450
  router.commitLocation({
1153
1451
  ...nextLocation,
1154
1452
  replace: true
@@ -1159,20 +1457,21 @@
1159
1457
  };
1160
1458
  }, [router.history]);
1161
1459
  useLayoutEffect$1(() => {
1162
- if (!isTransitioning && state.resolvedLocation !== state.location) {
1460
+ if (!isTransitioning && router.state.resolvedLocation !== router.state.location) {
1163
1461
  router.emit({
1164
1462
  type: 'onResolved',
1165
- fromLocation: state.resolvedLocation,
1166
- toLocation: state.location,
1167
- pathChanged: state.location.href !== state.resolvedLocation?.href
1463
+ fromLocation: router.state.resolvedLocation,
1464
+ toLocation: router.state.location,
1465
+ pathChanged: router.state.location.href !== router.state.resolvedLocation?.href
1168
1466
  });
1169
1467
  router.pendingMatches = [];
1170
- setState(s => ({
1468
+ router.__store.setState(s => ({
1171
1469
  ...s,
1470
+ isTransitioning: false,
1172
1471
  resolvedLocation: s.location
1173
1472
  }));
1174
1473
  }
1175
- });
1474
+ }, [isTransitioning]);
1176
1475
  useLayoutEffect$1(() => {
1177
1476
  if (!window.__TSR_DEHYDRATED__) {
1178
1477
  tryLoad();
@@ -1183,7 +1482,11 @@
1183
1482
  }, /*#__PURE__*/React__namespace.createElement(Matches, null));
1184
1483
  }
1185
1484
  function getRouteMatch(state, id) {
1186
- return [...state.pendingMatches, ...state.matches].find(d => d.id === id);
1485
+ return [...(state.pendingMatches ?? []), ...state.matches].find(d => d.id === id);
1486
+ }
1487
+ function useRouterState(opts) {
1488
+ const router = useRouter();
1489
+ return useStore(router.__store, opts?.select);
1187
1490
  }
1188
1491
  function useRouter() {
1189
1492
  const resolvedContext = typeof document !== 'undefined' ? window.__TSR_ROUTER_CONTEXT__ || routerContext : routerContext;
@@ -1546,9 +1849,6 @@
1546
1849
  // by the router provider once rendered. We provide these so that the
1547
1850
  // router can be used in a non-react environment if necessary
1548
1851
  startReactTransition = fn => fn();
1549
- setState = updater => {
1550
- this.state = functionalUpdate(updater, this.state);
1551
- };
1552
1852
  update = newOptions => {
1553
1853
  this.options = {
1554
1854
  ...this.options,
@@ -1563,10 +1863,20 @@
1563
1863
  this.routeTree = this.options.routeTree;
1564
1864
  this.buildRouteTree();
1565
1865
  }
1566
- if (!this.state) {
1567
- this.state = getInitialRouterState(this.latestLocation);
1866
+ if (!this.__store) {
1867
+ this.__store = new Store(getInitialRouterState(this.latestLocation), {
1868
+ onUpdate: () => {
1869
+ this.__store.state = {
1870
+ ...this.state,
1871
+ status: this.state.isTransitioning || this.state.isLoading ? 'pending' : 'idle'
1872
+ };
1873
+ }
1874
+ });
1568
1875
  }
1569
1876
  };
1877
+ get state() {
1878
+ return this.__store.state;
1879
+ }
1570
1880
  buildRouteTree = () => {
1571
1881
  this.routesById = {};
1572
1882
  this.routesByPath = {};
@@ -1838,7 +2148,7 @@
1838
2148
  getRouteMatch(this.state, id)?.abortController?.abort();
1839
2149
  };
1840
2150
  cancelMatches = () => {
1841
- this.state.matches.forEach(match => {
2151
+ this.state.pendingMatches?.forEach(match => {
1842
2152
  this.cancelMatch(match.id);
1843
2153
  });
1844
2154
  };
@@ -2029,6 +2339,12 @@
2029
2339
  }) => {
2030
2340
  let latestPromise;
2031
2341
  let firstBadMatchIndex;
2342
+ const updatePendingMatch = match => {
2343
+ this.__store.setState(s => ({
2344
+ ...s,
2345
+ pendingMatches: s.pendingMatches?.map(d => d.id === match.id ? match : d)
2346
+ }));
2347
+ };
2032
2348
 
2033
2349
  // Check each match middleware to see if the route can be accessed
2034
2350
  try {
@@ -2185,12 +2501,10 @@
2185
2501
  loadPromise
2186
2502
  };
2187
2503
  if (!preload) {
2188
- this.setState(s => ({
2189
- ...s,
2190
- matches: s.matches.map(d => d.id === match.id ? match : d)
2191
- }));
2504
+ updatePendingMatch(match);
2192
2505
  }
2193
2506
  let didShowPending = false;
2507
+ const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
2194
2508
  await new Promise(async resolve => {
2195
2509
  // If the route has a pending component and a pendingMs option,
2196
2510
  // forcefully show the pending component
@@ -2202,17 +2516,13 @@
2202
2516
  ...match,
2203
2517
  showPending: true
2204
2518
  };
2205
- this.setState(s => ({
2206
- ...s,
2207
- matches: s.matches.map(d => d.id === match.id ? match : d)
2208
- }));
2519
+ updatePendingMatch(match);
2209
2520
  resolve();
2210
2521
  });
2211
2522
  }
2212
2523
  try {
2213
2524
  const loaderData = await loadPromise;
2214
2525
  if (latestPromise = checkLatest()) return await latestPromise;
2215
- const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
2216
2526
  if (didShowPending && pendingMinMs) {
2217
2527
  await new Promise(r => setTimeout(r, pendingMinMs));
2218
2528
  }
@@ -2242,12 +2552,19 @@
2242
2552
  isFetching: false,
2243
2553
  updatedAt: Date.now()
2244
2554
  };
2555
+ } finally {
2556
+ // If we showed the pending component, that means
2557
+ // we already moved the pendingMatches to the matches
2558
+ // state, so we need to update that specific match
2559
+ if (didShowPending && pendingMinMs && match.showPending) {
2560
+ this.__store.setState(s => ({
2561
+ ...s,
2562
+ matches: s.matches?.map(d => d.id === match.id ? match : d)
2563
+ }));
2564
+ }
2245
2565
  }
2246
2566
  if (!preload) {
2247
- this.setState(s => ({
2248
- ...s,
2249
- matches: s.matches.map(d => d.id === match.id ? match : d)
2250
- }));
2567
+ updatePendingMatch(match);
2251
2568
  }
2252
2569
  resolve();
2253
2570
  });
@@ -2276,24 +2593,23 @@
2276
2593
  });
2277
2594
 
2278
2595
  // Match the routes
2279
- let matches = this.matchRoutes(next.pathname, next.search, {
2596
+ let pendingMatches = this.matchRoutes(next.pathname, next.search, {
2280
2597
  debug: true
2281
2598
  });
2282
- this.pendingMatches = matches;
2283
2599
  const previousMatches = this.state.matches;
2284
2600
 
2285
2601
  // Ingest the new matches
2286
- this.setState(s => ({
2602
+ this.__store.setState(s => ({
2287
2603
  ...s,
2288
- // status: 'pending',
2604
+ isLoading: true,
2289
2605
  location: next,
2290
- matches
2606
+ pendingMatches
2291
2607
  }));
2292
2608
  try {
2293
2609
  try {
2294
2610
  // Load the matches
2295
2611
  await this.loadMatches({
2296
- matches,
2612
+ matches: pendingMatches,
2297
2613
  checkLatest: () => this.checkLatest(promise),
2298
2614
  invalidate: opts?.invalidate
2299
2615
  });
@@ -2308,14 +2624,13 @@
2308
2624
  }
2309
2625
  const exitingMatchIds = previousMatches.filter(id => !this.pendingMatches.includes(id));
2310
2626
  const enteringMatchIds = this.pendingMatches.filter(id => !previousMatches.includes(id));
2311
- const stayingMatchIds = previousMatches.filter(id => this.pendingMatches.includes(id))
2312
-
2313
- // setState((s) => ({
2314
- // ...s,
2315
- // status: 'idle',
2316
- // resolvedLocation: s.location,
2317
- // matches,
2318
- // }))
2627
+ const stayingMatchIds = previousMatches.filter(id => this.pendingMatches.includes(id));
2628
+ this.__store.setState(s => ({
2629
+ ...s,
2630
+ isLoading: false,
2631
+ matches: pendingMatches,
2632
+ pendingMatches: undefined
2633
+ }))
2319
2634
 
2320
2635
  //
2321
2636
  ;
@@ -2470,9 +2785,6 @@
2470
2785
  return false;
2471
2786
  }
2472
2787
  const baseLocation = opts?.pending ? this.latestLocation : this.state.resolvedLocation;
2473
-
2474
- // const baseLocation = state.resolvedLocation
2475
-
2476
2788
  if (!baseLocation) {
2477
2789
  return false;
2478
2790
  }
@@ -2544,7 +2856,7 @@
2544
2856
  }
2545
2857
  return match;
2546
2858
  });
2547
- this.setState(s => {
2859
+ this.__store.setState(s => {
2548
2860
  return {
2549
2861
  ...s,
2550
2862
  matches: matches
@@ -2575,6 +2887,8 @@
2575
2887
  class PathParamError extends Error {}
2576
2888
  function getInitialRouterState(location) {
2577
2889
  return {
2890
+ isLoading: false,
2891
+ isTransitioning: false,
2578
2892
  status: 'idle',
2579
2893
  resolvedLocation: location,
2580
2894
  location,
@@ -2882,6 +3196,7 @@
2882
3196
  exports.useParams = useParams;
2883
3197
  exports.useRouteContext = useRouteContext;
2884
3198
  exports.useRouter = useRouter;
3199
+ exports.useRouterState = useRouterState;
2885
3200
  exports.useScrollRestoration = useScrollRestoration;
2886
3201
  exports.useSearch = useSearch;
2887
3202
  exports.useStableCallback = useStableCallback;