@tanstack/react-router 0.0.1-beta.234 → 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.
@@ -1,6 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  /// <reference types="node" />
3
3
  import { RouterHistory } from '@tanstack/history';
4
+ import { Store } from '@tanstack/store';
4
5
  import { AnySearchSchema, AnyRoute, AnyContext, AnyPathParams, RouteMask } from './route';
5
6
  import { FullSearchSchema, RoutesById, RoutesByPath } from './routeInfo';
6
7
  import { PickAsRequired, Updater, NonNullableUpdater } from './utils';
@@ -56,8 +57,10 @@ export interface RouterOptions<TRouteTree extends AnyRoute, TDehydrated extends
56
57
  }
57
58
  export interface RouterState<TRouteTree extends AnyRoute = AnyRoute> {
58
59
  status: 'pending' | 'idle';
60
+ isLoading: boolean;
61
+ isTransitioning: boolean;
59
62
  matches: RouteMatch<TRouteTree>[];
60
- pendingMatches: RouteMatch<TRouteTree>[];
63
+ pendingMatches?: RouteMatch<TRouteTree>[];
61
64
  location: ParsedLocation<FullSearchSchema<TRouteTree>>;
62
65
  resolvedLocation: ParsedLocation<FullSearchSchema<TRouteTree>>;
63
66
  lastUpdated: number;
@@ -122,7 +125,7 @@ export declare class Router<TRouteTree extends AnyRoute = AnyRoute, TDehydrated
122
125
  pendingMatches: AnyRouteMatch[];
123
126
  injectedHtml: InjectedHtmlEntry[];
124
127
  dehydratedData?: TDehydrated;
125
- state: RouterState<TRouteTree>;
128
+ __store: Store<RouterState<TRouteTree>>;
126
129
  options: PickAsRequired<RouterOptions<TRouteTree, TDehydrated>, 'stringifySearch' | 'parseSearch' | 'context'>;
127
130
  history: RouterHistory;
128
131
  latestLocation: ParsedLocation;
@@ -133,8 +136,8 @@ export declare class Router<TRouteTree extends AnyRoute = AnyRoute, TDehydrated
133
136
  flatRoutes: AnyRoute[];
134
137
  constructor(options: RouterConstructorOptions<TRouteTree, TDehydrated>);
135
138
  startReactTransition: (fn: () => void) => void;
136
- setState: (updater: NonNullableUpdater<RouterState<TRouteTree>>) => void;
137
139
  update: (newOptions: RouterConstructorOptions<TRouteTree, TDehydrated>) => void;
140
+ get state(): RouterState<TRouteTree>;
138
141
  buildRouteTree: () => void;
139
142
  subscribe: <TType extends keyof RouterEvents>(eventType: TType, fn: ListenerFn<RouterEvents[TType]>) => () => void;
140
143
  emit: (routerEvent: RouterEvent) => void;
@@ -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, {
@@ -934,12 +1200,10 @@
934
1200
 
935
1201
  function Matches() {
936
1202
  const {
937
- routesById,
938
- state
1203
+ routesById
939
1204
  } = useRouter();
940
- const {
941
- matches
942
- } = state;
1205
+ const routerState = useRouterState();
1206
+ const matches = routerState.pendingMatches?.some(d => d.showPending) ? routerState.pendingMatches : routerState.matches;
943
1207
  const locationKey = useRouterState().location.state.key;
944
1208
  const route = routesById[rootRouteId];
945
1209
  const errorComponent = React__namespace.useCallback(props => {
@@ -1085,7 +1349,8 @@
1085
1349
  const nearestMatchRouteId = nearestMatch?.routeId;
1086
1350
  const matchRouteId = useRouterState({
1087
1351
  select: state => {
1088
- const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
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);
1089
1354
  return match.routeId;
1090
1355
  }
1091
1356
  });
@@ -1094,7 +1359,8 @@
1094
1359
  }
1095
1360
  const matchSelection = useRouterState({
1096
1361
  select: state => {
1097
- const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
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);
1098
1364
  invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
1099
1365
  return opts?.select ? opts.select(match) : match;
1100
1366
  }
@@ -1106,7 +1372,8 @@
1106
1372
  const contextMatches = React__namespace.useContext(matchesContext);
1107
1373
  return useRouterState({
1108
1374
  select: state => {
1109
- const matches = state.matches.slice(state.matches.findIndex(d => d.id === contextMatches[0]?.id));
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));
1110
1377
  return opts?.select ? opts.select(matches) : matches;
1111
1378
  }
1112
1379
  });
@@ -1147,31 +1414,29 @@
1147
1414
  function RouterProviderInner({
1148
1415
  router
1149
1416
  }) {
1150
- const [preState, setState] = React__namespace.useState(() => router.state);
1151
1417
  const [isTransitioning, startReactTransition] = React__namespace.useTransition();
1152
- const isAnyTransitioning = isTransitioning || preState.matches.some(d => d.status === 'pending');
1153
- const state = React__namespace.useMemo(() => ({
1154
- ...preState,
1155
- status: isAnyTransitioning ? 'pending' : 'idle',
1156
- location: isTransitioning ? router.latestLocation : preState.location,
1157
- pendingMatches: router.pendingMatches
1158
- }), [preState, isTransitioning]);
1159
- router.setState = setState;
1160
- router.state = state;
1161
1418
  router.startReactTransition = startReactTransition;
1419
+ React__namespace.useEffect(() => {
1420
+ if (isTransitioning) {
1421
+ router.__store.setState(s => ({
1422
+ ...s,
1423
+ isTransitioning
1424
+ }));
1425
+ }
1426
+ }, [isTransitioning]);
1162
1427
  const tryLoad = () => {
1163
- startReactTransition(() => {
1164
- try {
1165
- router.load();
1166
- } catch (err) {
1167
- console.error(err);
1168
- }
1169
- });
1428
+ // startReactTransition(() => {
1429
+ try {
1430
+ router.load();
1431
+ } catch (err) {
1432
+ console.error(err);
1433
+ }
1434
+ // })
1170
1435
  };
1171
1436
  useLayoutEffect$1(() => {
1172
1437
  const unsub = router.history.subscribe(() => {
1173
1438
  router.latestLocation = router.parseLocation(router.latestLocation);
1174
- if (state.location !== router.latestLocation) {
1439
+ if (router.state.location !== router.latestLocation) {
1175
1440
  tryLoad();
1176
1441
  }
1177
1442
  });
@@ -1181,7 +1446,7 @@
1181
1446
  hash: true,
1182
1447
  state: true
1183
1448
  });
1184
- if (state.location.href !== nextLocation.href) {
1449
+ if (router.state.location.href !== nextLocation.href) {
1185
1450
  router.commitLocation({
1186
1451
  ...nextLocation,
1187
1452
  replace: true
@@ -1192,20 +1457,21 @@
1192
1457
  };
1193
1458
  }, [router.history]);
1194
1459
  useLayoutEffect$1(() => {
1195
- if (!isTransitioning && state.resolvedLocation !== state.location) {
1460
+ if (!isTransitioning && router.state.resolvedLocation !== router.state.location) {
1196
1461
  router.emit({
1197
1462
  type: 'onResolved',
1198
- fromLocation: state.resolvedLocation,
1199
- toLocation: state.location,
1200
- 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
1201
1466
  });
1202
1467
  router.pendingMatches = [];
1203
- setState(s => ({
1468
+ router.__store.setState(s => ({
1204
1469
  ...s,
1470
+ isTransitioning: false,
1205
1471
  resolvedLocation: s.location
1206
1472
  }));
1207
1473
  }
1208
- });
1474
+ }, [isTransitioning]);
1209
1475
  useLayoutEffect$1(() => {
1210
1476
  if (!window.__TSR_DEHYDRATED__) {
1211
1477
  tryLoad();
@@ -1216,14 +1482,11 @@
1216
1482
  }, /*#__PURE__*/React__namespace.createElement(Matches, null));
1217
1483
  }
1218
1484
  function getRouteMatch(state, id) {
1219
- return [...state.pendingMatches, ...state.matches].find(d => d.id === id);
1485
+ return [...(state.pendingMatches ?? []), ...state.matches].find(d => d.id === id);
1220
1486
  }
1221
1487
  function useRouterState(opts) {
1222
- const {
1223
- state
1224
- } = useRouter();
1225
- // return useStore(router.__store, opts?.select as any)
1226
- return opts?.select ? opts.select(state) : state;
1488
+ const router = useRouter();
1489
+ return useStore(router.__store, opts?.select);
1227
1490
  }
1228
1491
  function useRouter() {
1229
1492
  const resolvedContext = typeof document !== 'undefined' ? window.__TSR_ROUTER_CONTEXT__ || routerContext : routerContext;
@@ -1586,9 +1849,6 @@
1586
1849
  // by the router provider once rendered. We provide these so that the
1587
1850
  // router can be used in a non-react environment if necessary
1588
1851
  startReactTransition = fn => fn();
1589
- setState = updater => {
1590
- this.state = functionalUpdate(updater, this.state);
1591
- };
1592
1852
  update = newOptions => {
1593
1853
  this.options = {
1594
1854
  ...this.options,
@@ -1603,10 +1863,20 @@
1603
1863
  this.routeTree = this.options.routeTree;
1604
1864
  this.buildRouteTree();
1605
1865
  }
1606
- if (!this.state) {
1607
- 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
+ });
1608
1875
  }
1609
1876
  };
1877
+ get state() {
1878
+ return this.__store.state;
1879
+ }
1610
1880
  buildRouteTree = () => {
1611
1881
  this.routesById = {};
1612
1882
  this.routesByPath = {};
@@ -1878,7 +2148,7 @@
1878
2148
  getRouteMatch(this.state, id)?.abortController?.abort();
1879
2149
  };
1880
2150
  cancelMatches = () => {
1881
- this.state.matches.forEach(match => {
2151
+ this.state.pendingMatches?.forEach(match => {
1882
2152
  this.cancelMatch(match.id);
1883
2153
  });
1884
2154
  };
@@ -2069,6 +2339,12 @@
2069
2339
  }) => {
2070
2340
  let latestPromise;
2071
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
+ };
2072
2348
 
2073
2349
  // Check each match middleware to see if the route can be accessed
2074
2350
  try {
@@ -2225,12 +2501,10 @@
2225
2501
  loadPromise
2226
2502
  };
2227
2503
  if (!preload) {
2228
- this.setState(s => ({
2229
- ...s,
2230
- matches: s.matches.map(d => d.id === match.id ? match : d)
2231
- }));
2504
+ updatePendingMatch(match);
2232
2505
  }
2233
2506
  let didShowPending = false;
2507
+ const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
2234
2508
  await new Promise(async resolve => {
2235
2509
  // If the route has a pending component and a pendingMs option,
2236
2510
  // forcefully show the pending component
@@ -2242,17 +2516,13 @@
2242
2516
  ...match,
2243
2517
  showPending: true
2244
2518
  };
2245
- this.setState(s => ({
2246
- ...s,
2247
- matches: s.matches.map(d => d.id === match.id ? match : d)
2248
- }));
2519
+ updatePendingMatch(match);
2249
2520
  resolve();
2250
2521
  });
2251
2522
  }
2252
2523
  try {
2253
2524
  const loaderData = await loadPromise;
2254
2525
  if (latestPromise = checkLatest()) return await latestPromise;
2255
- const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
2256
2526
  if (didShowPending && pendingMinMs) {
2257
2527
  await new Promise(r => setTimeout(r, pendingMinMs));
2258
2528
  }
@@ -2282,12 +2552,19 @@
2282
2552
  isFetching: false,
2283
2553
  updatedAt: Date.now()
2284
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
+ }
2285
2565
  }
2286
2566
  if (!preload) {
2287
- this.setState(s => ({
2288
- ...s,
2289
- matches: s.matches.map(d => d.id === match.id ? match : d)
2290
- }));
2567
+ updatePendingMatch(match);
2291
2568
  }
2292
2569
  resolve();
2293
2570
  });
@@ -2316,24 +2593,23 @@
2316
2593
  });
2317
2594
 
2318
2595
  // Match the routes
2319
- let matches = this.matchRoutes(next.pathname, next.search, {
2596
+ let pendingMatches = this.matchRoutes(next.pathname, next.search, {
2320
2597
  debug: true
2321
2598
  });
2322
- this.pendingMatches = matches;
2323
2599
  const previousMatches = this.state.matches;
2324
2600
 
2325
2601
  // Ingest the new matches
2326
- this.setState(s => ({
2602
+ this.__store.setState(s => ({
2327
2603
  ...s,
2328
- // status: 'pending',
2604
+ isLoading: true,
2329
2605
  location: next,
2330
- matches
2606
+ pendingMatches
2331
2607
  }));
2332
2608
  try {
2333
2609
  try {
2334
2610
  // Load the matches
2335
2611
  await this.loadMatches({
2336
- matches,
2612
+ matches: pendingMatches,
2337
2613
  checkLatest: () => this.checkLatest(promise),
2338
2614
  invalidate: opts?.invalidate
2339
2615
  });
@@ -2348,14 +2624,13 @@
2348
2624
  }
2349
2625
  const exitingMatchIds = previousMatches.filter(id => !this.pendingMatches.includes(id));
2350
2626
  const enteringMatchIds = this.pendingMatches.filter(id => !previousMatches.includes(id));
2351
- const stayingMatchIds = previousMatches.filter(id => this.pendingMatches.includes(id))
2352
-
2353
- // setState((s) => ({
2354
- // ...s,
2355
- // status: 'idle',
2356
- // resolvedLocation: s.location,
2357
- // matches,
2358
- // }))
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
+ }))
2359
2634
 
2360
2635
  //
2361
2636
  ;
@@ -2510,9 +2785,6 @@
2510
2785
  return false;
2511
2786
  }
2512
2787
  const baseLocation = opts?.pending ? this.latestLocation : this.state.resolvedLocation;
2513
-
2514
- // const baseLocation = state.resolvedLocation
2515
-
2516
2788
  if (!baseLocation) {
2517
2789
  return false;
2518
2790
  }
@@ -2584,7 +2856,7 @@
2584
2856
  }
2585
2857
  return match;
2586
2858
  });
2587
- this.setState(s => {
2859
+ this.__store.setState(s => {
2588
2860
  return {
2589
2861
  ...s,
2590
2862
  matches: matches
@@ -2615,6 +2887,8 @@
2615
2887
  class PathParamError extends Error {}
2616
2888
  function getInitialRouterState(location) {
2617
2889
  return {
2890
+ isLoading: false,
2891
+ isTransitioning: false,
2618
2892
  status: 'idle',
2619
2893
  resolvedLocation: location,
2620
2894
  location,