@tanstack/react-router 0.0.1-beta.235 → 0.0.1-beta.237

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 +31 -18
  2. package/build/cjs/Matches.js.map +1 -1
  3. package/build/cjs/RouterProvider.js +45 -25
  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 +155 -92
  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 +423 -95
  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 +70 -35
  34. package/src/RouterProvider.tsx +68 -32
  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
  };
@@ -915,10 +1200,9 @@
915
1200
 
916
1201
  function Matches() {
917
1202
  const router = useRouter();
918
- const {
919
- matches
920
- } = router.state;
921
- const locationKey = router.state.location.state.key;
1203
+ const routerState = useRouterState();
1204
+ const matches = routerState.pendingMatches?.some(d => d.showPending) ? routerState.pendingMatches : routerState.matches;
1205
+ const locationKey = router.latestLocation.state.key;
922
1206
  const route = router.routesById[rootRouteId];
923
1207
  const errorComponent = React__namespace.useCallback(props => {
924
1208
  return /*#__PURE__*/React__namespace.createElement(ErrorComponent, {
@@ -1059,31 +1343,45 @@
1059
1343
  return !!params ? props.children : null;
1060
1344
  }
1061
1345
  function useMatch(opts) {
1062
- const router = useRouter();
1063
1346
  const nearestMatch = React__namespace.useContext(matchesContext)[0];
1064
1347
  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
- })();
1348
+ const matchRouteId = useRouterState({
1349
+ select: state => {
1350
+ const matches = state.pendingMatches?.some(d => d.showPending) ? state.pendingMatches : state.matches;
1351
+ const match = opts?.from ? matches.find(d => d.routeId === opts?.from) : matches.find(d => d.id === nearestMatch.id);
1352
+ return match.routeId;
1353
+ }
1354
+ });
1069
1355
  if (opts?.strict ?? true) {
1070
1356
  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
1357
  }
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
- })();
1358
+ const matchSelection = useRouterState({
1359
+ select: state => {
1360
+ const matches = state.pendingMatches?.some(d => d.showPending) ? state.pendingMatches : state.matches;
1361
+ const match = opts?.from ? matches.find(d => d.routeId === opts?.from) : matches.find(d => d.id === nearestMatch.id);
1362
+ invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
1363
+ return opts?.select ? opts.select(match) : match;
1364
+ }
1365
+ });
1077
1366
  return matchSelection;
1078
1367
  }
1079
1368
  const matchesContext = /*#__PURE__*/React__namespace.createContext(null);
1080
- function useMatches() {
1081
- const router = useRouter();
1369
+ function useMatches(opts) {
1082
1370
  const contextMatches = React__namespace.useContext(matchesContext);
1083
- return router.state.matches.slice(router.state.matches.findIndex(d => d.id === contextMatches[0]?.id));
1371
+ return useRouterState({
1372
+ select: state => {
1373
+ let matches = state.pendingMatches?.some(d => d.showPending) ? state.pendingMatches : state.matches;
1374
+ matches = matches.slice(matches.findIndex(d => d.id === contextMatches[0]?.id));
1375
+ return opts?.select ? opts.select(matches) : matches;
1376
+ }
1377
+ });
1084
1378
  }
1085
1379
  function useLoaderData(opts) {
1086
- return useMatch(opts)?.loaderData;
1380
+ const match = useMatch({
1381
+ ...opts,
1382
+ select: undefined
1383
+ });
1384
+ return typeof opts.select === 'function' ? opts.select(match?.loaderData) : match?.loaderData;
1087
1385
  }
1088
1386
 
1089
1387
  const routerContext = /*#__PURE__*/React__namespace.createContext(null);
@@ -1103,9 +1401,11 @@
1103
1401
  ...rest?.context
1104
1402
  }
1105
1403
  });
1106
- const inner = /*#__PURE__*/React__namespace.createElement(RouterProviderInner, {
1404
+ const inner = /*#__PURE__*/React__namespace.createElement(routerContext.Provider, {
1405
+ value: router
1406
+ }, /*#__PURE__*/React__namespace.createElement(RouterProviderInner, {
1107
1407
  router: router
1108
- });
1408
+ }));
1109
1409
  if (router.options.Wrap) {
1110
1410
  return /*#__PURE__*/React__namespace.createElement(router.options.Wrap, null, inner);
1111
1411
  }
@@ -1114,20 +1414,32 @@
1114
1414
  function RouterProviderInner({
1115
1415
  router
1116
1416
  }) {
1117
- const [preState, setState] = React__namespace.useState(() => router.state);
1417
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(Matches, null), /*#__PURE__*/React__namespace.createElement(Transitioner, null));
1418
+ }
1419
+ function Transitioner() {
1420
+ const router = useRouter();
1421
+ const routerState = useRouterState({
1422
+ select: s => pick(s, ['isLoading', 'location', 'resolvedLocation', 'isTransitioning'])
1423
+ });
1118
1424
  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
1425
  router.startReactTransition = startReactTransition;
1426
+ React__namespace.useEffect(() => {
1427
+ if (isTransitioning) {
1428
+ router.__store.setState(s => ({
1429
+ ...s,
1430
+ isTransitioning
1431
+ }));
1432
+ }
1433
+ }, [isTransitioning]);
1129
1434
  const tryLoad = () => {
1130
- startReactTransition(() => {
1435
+ const apply = cb => {
1436
+ if (!routerState.isTransitioning) {
1437
+ startReactTransition(() => cb());
1438
+ } else {
1439
+ cb();
1440
+ }
1441
+ };
1442
+ apply(() => {
1131
1443
  try {
1132
1444
  router.load();
1133
1445
  } catch (err) {
@@ -1138,7 +1450,7 @@
1138
1450
  useLayoutEffect$1(() => {
1139
1451
  const unsub = router.history.subscribe(() => {
1140
1452
  router.latestLocation = router.parseLocation(router.latestLocation);
1141
- if (state.location !== router.latestLocation) {
1453
+ if (routerState.location !== router.latestLocation) {
1142
1454
  tryLoad();
1143
1455
  }
1144
1456
  });
@@ -1148,7 +1460,7 @@
1148
1460
  hash: true,
1149
1461
  state: true
1150
1462
  });
1151
- if (state.location.href !== nextLocation.href) {
1463
+ if (routerState.location.href !== nextLocation.href) {
1152
1464
  router.commitLocation({
1153
1465
  ...nextLocation,
1154
1466
  replace: true
@@ -1159,31 +1471,35 @@
1159
1471
  };
1160
1472
  }, [router.history]);
1161
1473
  useLayoutEffect$1(() => {
1162
- if (!isTransitioning && state.resolvedLocation !== state.location) {
1474
+ if (!isTransitioning && !routerState.isLoading && routerState.resolvedLocation !== routerState.location) {
1475
+ console.log('onResolved', routerState.location);
1163
1476
  router.emit({
1164
1477
  type: 'onResolved',
1165
- fromLocation: state.resolvedLocation,
1166
- toLocation: state.location,
1167
- pathChanged: state.location.href !== state.resolvedLocation?.href
1478
+ fromLocation: routerState.resolvedLocation,
1479
+ toLocation: routerState.location,
1480
+ pathChanged: routerState.location.href !== routerState.resolvedLocation?.href
1168
1481
  });
1169
1482
  router.pendingMatches = [];
1170
- setState(s => ({
1483
+ router.__store.setState(s => ({
1171
1484
  ...s,
1485
+ isTransitioning: false,
1172
1486
  resolvedLocation: s.location
1173
1487
  }));
1174
1488
  }
1175
- });
1489
+ }, [isTransitioning, routerState.isLoading]);
1176
1490
  useLayoutEffect$1(() => {
1177
1491
  if (!window.__TSR_DEHYDRATED__) {
1178
1492
  tryLoad();
1179
1493
  }
1180
1494
  }, []);
1181
- return /*#__PURE__*/React__namespace.createElement(routerContext.Provider, {
1182
- value: router
1183
- }, /*#__PURE__*/React__namespace.createElement(Matches, null));
1495
+ return null;
1184
1496
  }
1185
1497
  function getRouteMatch(state, id) {
1186
- return [...state.pendingMatches, ...state.matches].find(d => d.id === id);
1498
+ return [...(state.pendingMatches ?? []), ...state.matches].find(d => d.id === id);
1499
+ }
1500
+ function useRouterState(opts) {
1501
+ const router = useRouter();
1502
+ return useStore(router.__store, opts?.select);
1187
1503
  }
1188
1504
  function useRouter() {
1189
1505
  const resolvedContext = typeof document !== 'undefined' ? window.__TSR_ROUTER_CONTEXT__ || routerContext : routerContext;
@@ -1546,9 +1862,6 @@
1546
1862
  // by the router provider once rendered. We provide these so that the
1547
1863
  // router can be used in a non-react environment if necessary
1548
1864
  startReactTransition = fn => fn();
1549
- setState = updater => {
1550
- this.state = functionalUpdate(updater, this.state);
1551
- };
1552
1865
  update = newOptions => {
1553
1866
  this.options = {
1554
1867
  ...this.options,
@@ -1563,10 +1876,20 @@
1563
1876
  this.routeTree = this.options.routeTree;
1564
1877
  this.buildRouteTree();
1565
1878
  }
1566
- if (!this.state) {
1567
- this.state = getInitialRouterState(this.latestLocation);
1879
+ if (!this.__store) {
1880
+ this.__store = new Store(getInitialRouterState(this.latestLocation), {
1881
+ onUpdate: () => {
1882
+ this.__store.state = {
1883
+ ...this.state,
1884
+ status: this.state.isTransitioning || this.state.isLoading ? 'pending' : 'idle'
1885
+ };
1886
+ }
1887
+ });
1568
1888
  }
1569
1889
  };
1890
+ get state() {
1891
+ return this.__store.state;
1892
+ }
1570
1893
  buildRouteTree = () => {
1571
1894
  this.routesById = {};
1572
1895
  this.routesByPath = {};
@@ -1838,7 +2161,7 @@
1838
2161
  getRouteMatch(this.state, id)?.abortController?.abort();
1839
2162
  };
1840
2163
  cancelMatches = () => {
1841
- this.state.matches.forEach(match => {
2164
+ this.state.pendingMatches?.forEach(match => {
1842
2165
  this.cancelMatch(match.id);
1843
2166
  });
1844
2167
  };
@@ -2029,6 +2352,12 @@
2029
2352
  }) => {
2030
2353
  let latestPromise;
2031
2354
  let firstBadMatchIndex;
2355
+ const updatePendingMatch = match => {
2356
+ this.__store.setState(s => ({
2357
+ ...s,
2358
+ pendingMatches: s.pendingMatches?.map(d => d.id === match.id ? match : d)
2359
+ }));
2360
+ };
2032
2361
 
2033
2362
  // Check each match middleware to see if the route can be accessed
2034
2363
  try {
@@ -2185,12 +2514,10 @@
2185
2514
  loadPromise
2186
2515
  };
2187
2516
  if (!preload) {
2188
- this.setState(s => ({
2189
- ...s,
2190
- matches: s.matches.map(d => d.id === match.id ? match : d)
2191
- }));
2517
+ updatePendingMatch(match);
2192
2518
  }
2193
2519
  let didShowPending = false;
2520
+ const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
2194
2521
  await new Promise(async resolve => {
2195
2522
  // If the route has a pending component and a pendingMs option,
2196
2523
  // forcefully show the pending component
@@ -2202,17 +2529,13 @@
2202
2529
  ...match,
2203
2530
  showPending: true
2204
2531
  };
2205
- this.setState(s => ({
2206
- ...s,
2207
- matches: s.matches.map(d => d.id === match.id ? match : d)
2208
- }));
2532
+ updatePendingMatch(match);
2209
2533
  resolve();
2210
2534
  });
2211
2535
  }
2212
2536
  try {
2213
2537
  const loaderData = await loadPromise;
2214
2538
  if (latestPromise = checkLatest()) return await latestPromise;
2215
- const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
2216
2539
  if (didShowPending && pendingMinMs) {
2217
2540
  await new Promise(r => setTimeout(r, pendingMinMs));
2218
2541
  }
@@ -2242,12 +2565,19 @@
2242
2565
  isFetching: false,
2243
2566
  updatedAt: Date.now()
2244
2567
  };
2568
+ } finally {
2569
+ // If we showed the pending component, that means
2570
+ // we already moved the pendingMatches to the matches
2571
+ // state, so we need to update that specific match
2572
+ if (didShowPending && pendingMinMs && match.showPending) {
2573
+ this.__store.setState(s => ({
2574
+ ...s,
2575
+ matches: s.matches?.map(d => d.id === match.id ? match : d)
2576
+ }));
2577
+ }
2245
2578
  }
2246
2579
  if (!preload) {
2247
- this.setState(s => ({
2248
- ...s,
2249
- matches: s.matches.map(d => d.id === match.id ? match : d)
2250
- }));
2580
+ updatePendingMatch(match);
2251
2581
  }
2252
2582
  resolve();
2253
2583
  });
@@ -2276,24 +2606,23 @@
2276
2606
  });
2277
2607
 
2278
2608
  // Match the routes
2279
- let matches = this.matchRoutes(next.pathname, next.search, {
2609
+ let pendingMatches = this.matchRoutes(next.pathname, next.search, {
2280
2610
  debug: true
2281
2611
  });
2282
- this.pendingMatches = matches;
2283
2612
  const previousMatches = this.state.matches;
2284
2613
 
2285
2614
  // Ingest the new matches
2286
- this.setState(s => ({
2615
+ this.__store.setState(s => ({
2287
2616
  ...s,
2288
- // status: 'pending',
2617
+ isLoading: true,
2289
2618
  location: next,
2290
- matches
2619
+ pendingMatches
2291
2620
  }));
2292
2621
  try {
2293
2622
  try {
2294
2623
  // Load the matches
2295
2624
  await this.loadMatches({
2296
- matches,
2625
+ matches: pendingMatches,
2297
2626
  checkLatest: () => this.checkLatest(promise),
2298
2627
  invalidate: opts?.invalidate
2299
2628
  });
@@ -2308,14 +2637,13 @@
2308
2637
  }
2309
2638
  const exitingMatchIds = previousMatches.filter(id => !this.pendingMatches.includes(id));
2310
2639
  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
- // }))
2640
+ const stayingMatchIds = previousMatches.filter(id => this.pendingMatches.includes(id));
2641
+ this.__store.setState(s => ({
2642
+ ...s,
2643
+ isLoading: false,
2644
+ matches: pendingMatches,
2645
+ pendingMatches: undefined
2646
+ }))
2319
2647
 
2320
2648
  //
2321
2649
  ;
@@ -2470,9 +2798,6 @@
2470
2798
  return false;
2471
2799
  }
2472
2800
  const baseLocation = opts?.pending ? this.latestLocation : this.state.resolvedLocation;
2473
-
2474
- // const baseLocation = state.resolvedLocation
2475
-
2476
2801
  if (!baseLocation) {
2477
2802
  return false;
2478
2803
  }
@@ -2544,7 +2869,7 @@
2544
2869
  }
2545
2870
  return match;
2546
2871
  });
2547
- this.setState(s => {
2872
+ this.__store.setState(s => {
2548
2873
  return {
2549
2874
  ...s,
2550
2875
  matches: matches
@@ -2575,6 +2900,8 @@
2575
2900
  class PathParamError extends Error {}
2576
2901
  function getInitialRouterState(location) {
2577
2902
  return {
2903
+ isLoading: false,
2904
+ isTransitioning: false,
2578
2905
  status: 'idle',
2579
2906
  resolvedLocation: location,
2580
2907
  location,
@@ -2882,6 +3209,7 @@
2882
3209
  exports.useParams = useParams;
2883
3210
  exports.useRouteContext = useRouteContext;
2884
3211
  exports.useRouter = useRouter;
3212
+ exports.useRouterState = useRouterState;
2885
3213
  exports.useScrollRestoration = useScrollRestoration;
2886
3214
  exports.useSearch = useSearch;
2887
3215
  exports.useStableCallback = useStableCallback;