@remix-run/router 0.0.0-experimental-a077dc2d → 0.0.0-experimental-e192105b
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.
- package/CHANGELOG.md +22 -0
- package/dist/router.cjs.js +135 -65
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +3 -0
- package/dist/router.js +131 -65
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +135 -65
- package/dist/router.umd.js.map +1 -1
- package/dist/router.umd.min.js +2 -2
- package/dist/router.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/router.ts +184 -93
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# `@remix-run/router`
|
|
2
2
|
|
|
3
|
+
## 1.11.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add a new `future.v7_fetcherPersist` flag to the `@remix-run/router` to change the persistence behavior of fetchers when `router.deleteFetcher` is called. Instead of being immediately cleaned up, fetchers will persist until they return to an `idle` state ([RFC](https://github.com/remix-run/remix/discussions/7698)) ([#10962](https://github.com/remix-run/react-router/pull/10962))
|
|
8
|
+
|
|
9
|
+
- This is sort of a long-standing bug fix as the `useFetchers()` API was always supposed to only reflect **in-flight** fetcher information for pending/optimistic UI -- it was not intended to reflect fetcher data or hang onto fetchers after they returned to an `idle` state
|
|
10
|
+
- Keep an eye out for the following specific behavioral changes when opting into this flag and check your app for compatibility:
|
|
11
|
+
- Fetchers that complete _while still mounted_ will no longer appear in `useFetchers()`. They served effectively no purpose in there since you can access the data via `useFetcher().data`).
|
|
12
|
+
- Fetchers that previously unmounted _while in-flight_ will not be immediately aborted and will instead be cleaned up once they return to an `idle` state. They will remain exposed via `useFetchers` while in-flight so you can still access pending/optimistic data after unmount.
|
|
13
|
+
|
|
14
|
+
- When `v7_fetcherPersist` is enabled, the router now performs ref-counting on fetcher keys via `getFetcher`/`deleteFetcher` so it knows when a given fetcher is totally unmounted from the UI ([#10977](https://github.com/remix-run/react-router/pull/10977))
|
|
15
|
+
|
|
16
|
+
- Once a fetcher has been totally unmounted, we can ignore post-processing of a persisted fetcher result such as a redirect or an error
|
|
17
|
+
- The router will also pass a new `deletedFetchers` array to the subscriber callbacks so that the UI layer can remove associated fetcher data
|
|
18
|
+
|
|
19
|
+
- Add support for optional path segments in `matchPath` ([#10768](https://github.com/remix-run/react-router/pull/10768))
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Fix `router.getFetcher`/`router.deleteFetcher` type definitions which incorrectly specified `key` as an optional parameter ([#10960](https://github.com/remix-run/react-router/pull/10960))
|
|
24
|
+
|
|
3
25
|
## 1.10.0
|
|
4
26
|
|
|
5
27
|
### Minor Changes
|
package/dist/router.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @remix-run/router v0.0.0-experimental-
|
|
2
|
+
* @remix-run/router v0.0.0-experimental-e192105b
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -1739,6 +1739,9 @@ function createRouter(init) {
|
|
|
1739
1739
|
// Should the current navigation enable document.startViewTransition?
|
|
1740
1740
|
let pendingViewTransitionEnabled = false;
|
|
1741
1741
|
|
|
1742
|
+
// Should the current navigation use flushSync for state updates?
|
|
1743
|
+
let pendingFlushSync = false;
|
|
1744
|
+
|
|
1742
1745
|
// Store applied view transitions so we can apply them on POP
|
|
1743
1746
|
let appliedViewTransitions = new Map();
|
|
1744
1747
|
|
|
@@ -1783,6 +1786,9 @@ function createRouter(init) {
|
|
|
1783
1786
|
// Most recent href/match for fetcher.load calls for fetchers
|
|
1784
1787
|
let fetchLoadMatches = new Map();
|
|
1785
1788
|
|
|
1789
|
+
// Ref-count mounted fetchers so we know when it's ok to clean them up
|
|
1790
|
+
let activeFetchers = new Map();
|
|
1791
|
+
|
|
1786
1792
|
// Fetchers that have requested a delete when using v7_fetcherPersist,
|
|
1787
1793
|
// they'll be officially removed after they return to idle
|
|
1788
1794
|
let deletedFetchers = new Set();
|
|
@@ -1849,6 +1855,8 @@ function createRouter(init) {
|
|
|
1849
1855
|
blockers.set(blockerKey, IDLE_BLOCKER);
|
|
1850
1856
|
updateState({
|
|
1851
1857
|
blockers
|
|
1858
|
+
}, {
|
|
1859
|
+
flushSync: false
|
|
1852
1860
|
});
|
|
1853
1861
|
}
|
|
1854
1862
|
});
|
|
@@ -1897,29 +1905,42 @@ function createRouter(init) {
|
|
|
1897
1905
|
}
|
|
1898
1906
|
|
|
1899
1907
|
// Update our state and notify the calling context of the change
|
|
1900
|
-
function updateState(newState,
|
|
1908
|
+
function updateState(newState, opts) {
|
|
1901
1909
|
state = _extends({}, state, newState);
|
|
1902
|
-
subscribers.forEach(subscriber => subscriber(state, {
|
|
1903
|
-
unstable_viewTransitionOpts: viewTransitionOpts
|
|
1904
|
-
}));
|
|
1905
1910
|
|
|
1906
|
-
//
|
|
1911
|
+
// Prep fetcher cleanup so we can tell the UI which fetcher data entries
|
|
1912
|
+
// can be removed
|
|
1913
|
+
let completedFetchers = [];
|
|
1914
|
+
let deletedFetchersKeys = [];
|
|
1907
1915
|
if (future.v7_fetcherPersist) {
|
|
1908
1916
|
state.fetchers.forEach((fetcher, key) => {
|
|
1909
1917
|
if (fetcher.state === "idle") {
|
|
1910
1918
|
if (deletedFetchers.has(key)) {
|
|
1911
|
-
//
|
|
1912
|
-
|
|
1913
|
-
deleteFetcher(key);
|
|
1919
|
+
// Unmounted from the UI and can be totally removed
|
|
1920
|
+
deletedFetchersKeys.push(key);
|
|
1914
1921
|
} else {
|
|
1915
|
-
//
|
|
1916
|
-
//
|
|
1917
|
-
|
|
1918
|
-
state.fetchers.delete(key);
|
|
1922
|
+
// Returned to idle but still mounted in the UI, so semi-remains for
|
|
1923
|
+
// revalidations and such
|
|
1924
|
+
completedFetchers.push(key);
|
|
1919
1925
|
}
|
|
1920
1926
|
}
|
|
1921
1927
|
});
|
|
1922
1928
|
}
|
|
1929
|
+
|
|
1930
|
+
// Iterate over a local copy so that if flushSync is used and we end up
|
|
1931
|
+
// removing and adding a new subscriber due to the useCallback dependencies,
|
|
1932
|
+
// we don't get ourselves into a loop calling the new subscriber immediately
|
|
1933
|
+
[...subscribers].forEach(subscriber => subscriber(state, {
|
|
1934
|
+
deletedFetchers: deletedFetchersKeys,
|
|
1935
|
+
unstable_viewTransitionOpts: opts.viewTransitionOpts,
|
|
1936
|
+
unstable_flushSync: opts.flushSync
|
|
1937
|
+
}));
|
|
1938
|
+
|
|
1939
|
+
// Remove idle fetchers from state since we only care about in-flight fetchers.
|
|
1940
|
+
if (future.v7_fetcherPersist) {
|
|
1941
|
+
completedFetchers.forEach(key => state.fetchers.delete(key));
|
|
1942
|
+
deletedFetchersKeys.forEach(key => deleteFetcher(key));
|
|
1943
|
+
}
|
|
1923
1944
|
}
|
|
1924
1945
|
|
|
1925
1946
|
// Complete a navigation returning the state.navigation back to the IDLE_NAVIGATION
|
|
@@ -2019,12 +2040,16 @@ function createRouter(init) {
|
|
|
2019
2040
|
restoreScrollPosition: getSavedScrollPosition(location, newState.matches || state.matches),
|
|
2020
2041
|
preventScrollReset,
|
|
2021
2042
|
blockers
|
|
2022
|
-
}),
|
|
2043
|
+
}), {
|
|
2044
|
+
viewTransitionOpts,
|
|
2045
|
+
flushSync: pendingFlushSync
|
|
2046
|
+
});
|
|
2023
2047
|
|
|
2024
2048
|
// Reset stateful navigation vars
|
|
2025
2049
|
pendingAction = Action.Pop;
|
|
2026
2050
|
pendingPreventScrollReset = false;
|
|
2027
2051
|
pendingViewTransitionEnabled = false;
|
|
2052
|
+
pendingFlushSync = false;
|
|
2028
2053
|
isUninterruptedRevalidation = false;
|
|
2029
2054
|
isRevalidationRequired = false;
|
|
2030
2055
|
cancelledDeferredRoutes = [];
|
|
@@ -2065,6 +2090,7 @@ function createRouter(init) {
|
|
|
2065
2090
|
historyAction = Action.Replace;
|
|
2066
2091
|
}
|
|
2067
2092
|
let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : undefined;
|
|
2093
|
+
let flushSync = (opts && opts.unstable_flushSync) === true;
|
|
2068
2094
|
let blockerKey = shouldBlockNavigation({
|
|
2069
2095
|
currentLocation,
|
|
2070
2096
|
nextLocation,
|
|
@@ -2090,6 +2116,8 @@ function createRouter(init) {
|
|
|
2090
2116
|
blockers.set(blockerKey, IDLE_BLOCKER);
|
|
2091
2117
|
updateState({
|
|
2092
2118
|
blockers
|
|
2119
|
+
}, {
|
|
2120
|
+
flushSync
|
|
2093
2121
|
});
|
|
2094
2122
|
}
|
|
2095
2123
|
});
|
|
@@ -2102,7 +2130,8 @@ function createRouter(init) {
|
|
|
2102
2130
|
pendingError: error,
|
|
2103
2131
|
preventScrollReset,
|
|
2104
2132
|
replace: opts && opts.replace,
|
|
2105
|
-
enableViewTransition: opts && opts.unstable_viewTransition
|
|
2133
|
+
enableViewTransition: opts && opts.unstable_viewTransition,
|
|
2134
|
+
flushSync
|
|
2106
2135
|
});
|
|
2107
2136
|
}
|
|
2108
2137
|
|
|
@@ -2113,6 +2142,8 @@ function createRouter(init) {
|
|
|
2113
2142
|
interruptActiveLoads();
|
|
2114
2143
|
updateState({
|
|
2115
2144
|
revalidation: "loading"
|
|
2145
|
+
}, {
|
|
2146
|
+
flushSync: false
|
|
2116
2147
|
});
|
|
2117
2148
|
|
|
2118
2149
|
// If we're currently submitting an action, we don't need to start a new
|
|
@@ -2156,6 +2187,7 @@ function createRouter(init) {
|
|
|
2156
2187
|
saveScrollPosition(state.location, state.matches);
|
|
2157
2188
|
pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;
|
|
2158
2189
|
pendingViewTransitionEnabled = (opts && opts.enableViewTransition) === true;
|
|
2190
|
+
pendingFlushSync = (opts && opts.flushSync) === true;
|
|
2159
2191
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
2160
2192
|
let loadingNavigation = opts && opts.overrideNavigation;
|
|
2161
2193
|
let matches = matchRoutes(routesToUse, location, basename);
|
|
@@ -2261,6 +2293,8 @@ function createRouter(init) {
|
|
|
2261
2293
|
let navigation = getSubmittingNavigation(location, submission);
|
|
2262
2294
|
updateState({
|
|
2263
2295
|
navigation
|
|
2296
|
+
}, {
|
|
2297
|
+
flushSync: pendingFlushSync
|
|
2264
2298
|
});
|
|
2265
2299
|
|
|
2266
2300
|
// Call our action and get the result
|
|
@@ -2388,7 +2422,9 @@ function createRouter(init) {
|
|
|
2388
2422
|
actionData
|
|
2389
2423
|
} : {}, revalidatingFetchers.length > 0 ? {
|
|
2390
2424
|
fetchers: new Map(state.fetchers)
|
|
2391
|
-
} : {})
|
|
2425
|
+
} : {}), {
|
|
2426
|
+
flushSync: pendingFlushSync
|
|
2427
|
+
});
|
|
2392
2428
|
}
|
|
2393
2429
|
revalidatingFetchers.forEach(rf => {
|
|
2394
2430
|
if (fetchControllers.has(rf.key)) {
|
|
@@ -2471,9 +2507,6 @@ function createRouter(init) {
|
|
|
2471
2507
|
fetchers: new Map(state.fetchers)
|
|
2472
2508
|
} : {});
|
|
2473
2509
|
}
|
|
2474
|
-
function getFetcher(key) {
|
|
2475
|
-
return state.fetchers.get(key) || IDLE_FETCHER;
|
|
2476
|
-
}
|
|
2477
2510
|
|
|
2478
2511
|
// Trigger a fetcher load/submit for the given fetcher key
|
|
2479
2512
|
function fetch(key, routeId, href, opts) {
|
|
@@ -2481,13 +2514,16 @@ function createRouter(init) {
|
|
|
2481
2514
|
throw new Error("router.fetch() was called during the server render, but it shouldn't be. " + "You are likely calling a useFetcher() method in the body of your component. " + "Try moving it to a useEffect or a callback.");
|
|
2482
2515
|
}
|
|
2483
2516
|
if (fetchControllers.has(key)) abortFetcher(key);
|
|
2517
|
+
let flushSync = (opts && opts.unstable_flushSync) === true;
|
|
2484
2518
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
2485
2519
|
let normalizedPath = normalizeTo(state.location, state.matches, basename, future.v7_prependBasename, href, routeId, opts == null ? void 0 : opts.relative);
|
|
2486
2520
|
let matches = matchRoutes(routesToUse, normalizedPath, basename);
|
|
2487
2521
|
if (!matches) {
|
|
2488
2522
|
setFetcherError(key, routeId, getInternalRouterError(404, {
|
|
2489
2523
|
pathname: normalizedPath
|
|
2490
|
-
})
|
|
2524
|
+
}), {
|
|
2525
|
+
flushSync
|
|
2526
|
+
});
|
|
2491
2527
|
return;
|
|
2492
2528
|
}
|
|
2493
2529
|
let {
|
|
@@ -2496,13 +2532,15 @@ function createRouter(init) {
|
|
|
2496
2532
|
error
|
|
2497
2533
|
} = normalizeNavigateOptions(future.v7_normalizeFormMethod, true, normalizedPath, opts);
|
|
2498
2534
|
if (error) {
|
|
2499
|
-
setFetcherError(key, routeId, error
|
|
2535
|
+
setFetcherError(key, routeId, error, {
|
|
2536
|
+
flushSync
|
|
2537
|
+
});
|
|
2500
2538
|
return;
|
|
2501
2539
|
}
|
|
2502
2540
|
let match = getTargetMatch(matches, path);
|
|
2503
2541
|
pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;
|
|
2504
2542
|
if (submission && isMutationMethod(submission.formMethod)) {
|
|
2505
|
-
handleFetcherAction(key, routeId, path, match, matches, submission);
|
|
2543
|
+
handleFetcherAction(key, routeId, path, match, matches, flushSync, submission);
|
|
2506
2544
|
return;
|
|
2507
2545
|
}
|
|
2508
2546
|
|
|
@@ -2512,12 +2550,12 @@ function createRouter(init) {
|
|
|
2512
2550
|
routeId,
|
|
2513
2551
|
path
|
|
2514
2552
|
});
|
|
2515
|
-
handleFetcherLoader(key, routeId, path, match, matches, submission);
|
|
2553
|
+
handleFetcherLoader(key, routeId, path, match, matches, flushSync, submission);
|
|
2516
2554
|
}
|
|
2517
2555
|
|
|
2518
2556
|
// Call the action for the matched fetcher.submit(), and then handle redirects,
|
|
2519
2557
|
// errors, and revalidation
|
|
2520
|
-
async function handleFetcherAction(key, routeId, path, match, requestMatches, submission) {
|
|
2558
|
+
async function handleFetcherAction(key, routeId, path, match, requestMatches, flushSync, submission) {
|
|
2521
2559
|
interruptActiveLoads();
|
|
2522
2560
|
fetchLoadMatches.delete(key);
|
|
2523
2561
|
if (!match.route.action && !match.route.lazy) {
|
|
@@ -2526,16 +2564,16 @@ function createRouter(init) {
|
|
|
2526
2564
|
pathname: path,
|
|
2527
2565
|
routeId: routeId
|
|
2528
2566
|
});
|
|
2529
|
-
setFetcherError(key, routeId, error
|
|
2567
|
+
setFetcherError(key, routeId, error, {
|
|
2568
|
+
flushSync
|
|
2569
|
+
});
|
|
2530
2570
|
return;
|
|
2531
2571
|
}
|
|
2532
2572
|
|
|
2533
2573
|
// Put this fetcher into it's submitting state
|
|
2534
2574
|
let existingFetcher = state.fetchers.get(key);
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
updateState({
|
|
2538
|
-
fetchers: new Map(state.fetchers)
|
|
2575
|
+
updateFetcherState(key, getSubmittingFetcher(submission, existingFetcher), {
|
|
2576
|
+
flushSync
|
|
2539
2577
|
});
|
|
2540
2578
|
|
|
2541
2579
|
// Call the action for the fetcher
|
|
@@ -2545,13 +2583,19 @@ function createRouter(init) {
|
|
|
2545
2583
|
let originatingLoadId = incrementingLoadId;
|
|
2546
2584
|
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, manifest, mapRouteProperties, basename);
|
|
2547
2585
|
if (fetchRequest.signal.aborted) {
|
|
2548
|
-
// We can delete this so long as we weren't aborted by
|
|
2586
|
+
// We can delete this so long as we weren't aborted by our own fetcher
|
|
2549
2587
|
// re-submit which would have put _new_ controller is in fetchControllers
|
|
2550
2588
|
if (fetchControllers.get(key) === abortController) {
|
|
2551
2589
|
fetchControllers.delete(key);
|
|
2552
2590
|
}
|
|
2553
2591
|
return;
|
|
2554
2592
|
}
|
|
2593
|
+
if (deletedFetchers.has(key)) {
|
|
2594
|
+
updateFetcherState(key, getDoneFetcher(undefined), {
|
|
2595
|
+
flushSync
|
|
2596
|
+
});
|
|
2597
|
+
return;
|
|
2598
|
+
}
|
|
2555
2599
|
if (isRedirectResult(actionResult)) {
|
|
2556
2600
|
fetchControllers.delete(key);
|
|
2557
2601
|
if (pendingNavigationLoadId > originatingLoadId) {
|
|
@@ -2559,18 +2603,14 @@ function createRouter(init) {
|
|
|
2559
2603
|
// should take precedence over this redirect navigation. We already
|
|
2560
2604
|
// set isRevalidationRequired so all loaders for the new route should
|
|
2561
2605
|
// fire unless opted out via shouldRevalidate
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
updateState({
|
|
2565
|
-
fetchers: new Map(state.fetchers)
|
|
2606
|
+
updateFetcherState(key, getDoneFetcher(undefined), {
|
|
2607
|
+
flushSync
|
|
2566
2608
|
});
|
|
2567
2609
|
return;
|
|
2568
2610
|
} else {
|
|
2569
2611
|
fetchRedirectIds.add(key);
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
updateState({
|
|
2573
|
-
fetchers: new Map(state.fetchers)
|
|
2612
|
+
updateFetcherState(key, getLoadingFetcher(submission), {
|
|
2613
|
+
flushSync
|
|
2574
2614
|
});
|
|
2575
2615
|
return startRedirectNavigation(state, actionResult, {
|
|
2576
2616
|
fetcherSubmission: submission
|
|
@@ -2580,7 +2620,9 @@ function createRouter(init) {
|
|
|
2580
2620
|
|
|
2581
2621
|
// Process any non-redirect errors thrown
|
|
2582
2622
|
if (isErrorResult(actionResult)) {
|
|
2583
|
-
setFetcherError(key, routeId, actionResult.error
|
|
2623
|
+
setFetcherError(key, routeId, actionResult.error, {
|
|
2624
|
+
flushSync
|
|
2625
|
+
});
|
|
2584
2626
|
return;
|
|
2585
2627
|
}
|
|
2586
2628
|
if (isDeferredResult(actionResult)) {
|
|
@@ -2622,6 +2664,8 @@ function createRouter(init) {
|
|
|
2622
2664
|
});
|
|
2623
2665
|
updateState({
|
|
2624
2666
|
fetchers: new Map(state.fetchers)
|
|
2667
|
+
}, {
|
|
2668
|
+
flushSync
|
|
2625
2669
|
});
|
|
2626
2670
|
let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach(rf => abortFetcher(rf.key));
|
|
2627
2671
|
abortController.signal.addEventListener("abort", abortPendingFetchRevalidations);
|
|
@@ -2683,19 +2727,18 @@ function createRouter(init) {
|
|
|
2683
2727
|
errors,
|
|
2684
2728
|
loaderData: mergeLoaderData(state.loaderData, loaderData, matches, errors),
|
|
2685
2729
|
fetchers: new Map(state.fetchers)
|
|
2730
|
+
}, {
|
|
2731
|
+
flushSync
|
|
2686
2732
|
});
|
|
2687
2733
|
isRevalidationRequired = false;
|
|
2688
2734
|
}
|
|
2689
2735
|
}
|
|
2690
2736
|
|
|
2691
2737
|
// Call the matched loader for fetcher.load(), handling redirects, errors, etc.
|
|
2692
|
-
async function handleFetcherLoader(key, routeId, path, match, matches, submission) {
|
|
2738
|
+
async function handleFetcherLoader(key, routeId, path, match, matches, flushSync, submission) {
|
|
2693
2739
|
let existingFetcher = state.fetchers.get(key);
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
state.fetchers.set(key, loadingFetcher);
|
|
2697
|
-
updateState({
|
|
2698
|
-
fetchers: new Map(state.fetchers)
|
|
2740
|
+
updateFetcherState(key, getLoadingFetcher(submission, existingFetcher ? existingFetcher.data : undefined), {
|
|
2741
|
+
flushSync
|
|
2699
2742
|
});
|
|
2700
2743
|
|
|
2701
2744
|
// Call the loader for this fetcher route match
|
|
@@ -2721,16 +2764,20 @@ function createRouter(init) {
|
|
|
2721
2764
|
if (fetchRequest.signal.aborted) {
|
|
2722
2765
|
return;
|
|
2723
2766
|
}
|
|
2767
|
+
if (deletedFetchers.has(key)) {
|
|
2768
|
+
updateFetcherState(key, getDoneFetcher(undefined), {
|
|
2769
|
+
flushSync
|
|
2770
|
+
});
|
|
2771
|
+
return;
|
|
2772
|
+
}
|
|
2724
2773
|
|
|
2725
2774
|
// If the loader threw a redirect Response, start a new REPLACE navigation
|
|
2726
2775
|
if (isRedirectResult(result)) {
|
|
2727
2776
|
if (pendingNavigationLoadId > originatingLoadId) {
|
|
2728
2777
|
// A new navigation was kicked off after our loader started, so that
|
|
2729
2778
|
// should take precedence over this redirect navigation
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
updateState({
|
|
2733
|
-
fetchers: new Map(state.fetchers)
|
|
2779
|
+
updateFetcherState(key, getDoneFetcher(undefined), {
|
|
2780
|
+
flushSync
|
|
2734
2781
|
});
|
|
2735
2782
|
return;
|
|
2736
2783
|
} else {
|
|
@@ -2742,26 +2789,16 @@ function createRouter(init) {
|
|
|
2742
2789
|
|
|
2743
2790
|
// Process any non-redirect errors thrown
|
|
2744
2791
|
if (isErrorResult(result)) {
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
// TODO: In remix, this would reset to IDLE_NAVIGATION if it was a catch -
|
|
2748
|
-
// do we need to behave any differently with our non-redirect errors?
|
|
2749
|
-
// What if it was a non-redirect Response?
|
|
2750
|
-
updateState({
|
|
2751
|
-
fetchers: new Map(state.fetchers),
|
|
2752
|
-
errors: {
|
|
2753
|
-
[boundaryMatch.route.id]: result.error
|
|
2754
|
-
}
|
|
2792
|
+
setFetcherError(key, routeId, result.error, {
|
|
2793
|
+
flushSync
|
|
2755
2794
|
});
|
|
2756
2795
|
return;
|
|
2757
2796
|
}
|
|
2758
2797
|
invariant(!isDeferredResult(result), "Unhandled fetcher deferred data");
|
|
2759
2798
|
|
|
2760
2799
|
// Put the fetcher back into an idle state
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
updateState({
|
|
2764
|
-
fetchers: new Map(state.fetchers)
|
|
2800
|
+
updateFetcherState(key, getDoneFetcher(result.data), {
|
|
2801
|
+
flushSync
|
|
2765
2802
|
});
|
|
2766
2803
|
}
|
|
2767
2804
|
|
|
@@ -2903,7 +2940,15 @@ function createRouter(init) {
|
|
|
2903
2940
|
}
|
|
2904
2941
|
});
|
|
2905
2942
|
}
|
|
2906
|
-
function
|
|
2943
|
+
function updateFetcherState(key, fetcher, opts) {
|
|
2944
|
+
state.fetchers.set(key, fetcher);
|
|
2945
|
+
updateState({
|
|
2946
|
+
fetchers: new Map(state.fetchers)
|
|
2947
|
+
}, {
|
|
2948
|
+
flushSync: opts.flushSync
|
|
2949
|
+
});
|
|
2950
|
+
}
|
|
2951
|
+
function setFetcherError(key, routeId, error, opts) {
|
|
2907
2952
|
let boundaryMatch = findNearestBoundary(state.matches, routeId);
|
|
2908
2953
|
deleteFetcher(key);
|
|
2909
2954
|
updateState({
|
|
@@ -2911,8 +2956,21 @@ function createRouter(init) {
|
|
|
2911
2956
|
[boundaryMatch.route.id]: error
|
|
2912
2957
|
},
|
|
2913
2958
|
fetchers: new Map(state.fetchers)
|
|
2959
|
+
}, {
|
|
2960
|
+
flushSync: opts.flushSync
|
|
2914
2961
|
});
|
|
2915
2962
|
}
|
|
2963
|
+
function getFetcher(key) {
|
|
2964
|
+
if (future.v7_fetcherPersist) {
|
|
2965
|
+
activeFetchers.set(key, (activeFetchers.get(key) || 0) + 1);
|
|
2966
|
+
// If this fetcher was previously marked for deletion, unmark it since we
|
|
2967
|
+
// have a new instance
|
|
2968
|
+
if (deletedFetchers.has(key)) {
|
|
2969
|
+
deletedFetchers.delete(key);
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
return state.fetchers.get(key) || IDLE_FETCHER;
|
|
2973
|
+
}
|
|
2916
2974
|
function deleteFetcher(key) {
|
|
2917
2975
|
let fetcher = state.fetchers.get(key);
|
|
2918
2976
|
// Don't abort the controller if this is a deletion of a fetcher.submit()
|
|
@@ -2929,12 +2987,20 @@ function createRouter(init) {
|
|
|
2929
2987
|
}
|
|
2930
2988
|
function deleteFetcherAndUpdateState(key) {
|
|
2931
2989
|
if (future.v7_fetcherPersist) {
|
|
2932
|
-
|
|
2990
|
+
let count = (activeFetchers.get(key) || 0) - 1;
|
|
2991
|
+
if (count <= 0) {
|
|
2992
|
+
activeFetchers.delete(key);
|
|
2993
|
+
deletedFetchers.add(key);
|
|
2994
|
+
} else {
|
|
2995
|
+
activeFetchers.set(key, count);
|
|
2996
|
+
}
|
|
2933
2997
|
} else {
|
|
2934
2998
|
deleteFetcher(key);
|
|
2935
2999
|
}
|
|
2936
3000
|
updateState({
|
|
2937
3001
|
fetchers: new Map(state.fetchers)
|
|
3002
|
+
}, {
|
|
3003
|
+
flushSync: false
|
|
2938
3004
|
});
|
|
2939
3005
|
}
|
|
2940
3006
|
function abortFetcher(key) {
|
|
@@ -3004,6 +3070,8 @@ function createRouter(init) {
|
|
|
3004
3070
|
blockers.set(key, newBlocker);
|
|
3005
3071
|
updateState({
|
|
3006
3072
|
blockers
|
|
3073
|
+
}, {
|
|
3074
|
+
flushSync: false
|
|
3007
3075
|
});
|
|
3008
3076
|
}
|
|
3009
3077
|
function shouldBlockNavigation(_ref2) {
|
|
@@ -3071,6 +3139,8 @@ function createRouter(init) {
|
|
|
3071
3139
|
if (y != null) {
|
|
3072
3140
|
updateState({
|
|
3073
3141
|
restoreScrollPosition: y
|
|
3142
|
+
}, {
|
|
3143
|
+
flushSync: false
|
|
3074
3144
|
});
|
|
3075
3145
|
}
|
|
3076
3146
|
}
|