@remix-run/router 1.11.0-pre.0 → 1.11.0-pre.1
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 +9 -0
- package/dist/router.cjs.js +54 -25
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +1 -0
- package/dist/router.js +52 -25
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +54 -25
- 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 +56 -24
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# `@remix-run/router`
|
|
2
2
|
|
|
3
|
+
## 1.11.0-pre.1
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 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))
|
|
8
|
+
|
|
9
|
+
- Once a fetcher has been totally unmounted, we can ignore post-processing of a persisted fetcher result such as a redirect or an error
|
|
10
|
+
- The router will also pass a new `deletedFetchers` array to the subscriber callbacks so that the UI layer can remove associated fetcher data
|
|
11
|
+
|
|
3
12
|
## 1.11.0-pre.0
|
|
4
13
|
|
|
5
14
|
### Minor Changes
|
package/dist/router.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @remix-run/router v1.11.0-pre.
|
|
2
|
+
* @remix-run/router v1.11.0-pre.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -1783,6 +1783,9 @@ function createRouter(init) {
|
|
|
1783
1783
|
// Most recent href/match for fetcher.load calls for fetchers
|
|
1784
1784
|
let fetchLoadMatches = new Map();
|
|
1785
1785
|
|
|
1786
|
+
// Ref-count mounted fetchers so we know when it's ok to clean them up
|
|
1787
|
+
let activeFetchers = new Map();
|
|
1788
|
+
|
|
1786
1789
|
// Fetchers that have requested a delete when using v7_fetcherPersist,
|
|
1787
1790
|
// they'll be officially removed after they return to idle
|
|
1788
1791
|
let deletedFetchers = new Set();
|
|
@@ -1899,27 +1902,35 @@ function createRouter(init) {
|
|
|
1899
1902
|
// Update our state and notify the calling context of the change
|
|
1900
1903
|
function updateState(newState, viewTransitionOpts) {
|
|
1901
1904
|
state = _extends({}, state, newState);
|
|
1902
|
-
subscribers.forEach(subscriber => subscriber(state, {
|
|
1903
|
-
unstable_viewTransitionOpts: viewTransitionOpts
|
|
1904
|
-
}));
|
|
1905
1905
|
|
|
1906
|
-
//
|
|
1906
|
+
// Prep fetcher cleanup so we can tell the UI which fetcher data entries
|
|
1907
|
+
// can be removed
|
|
1908
|
+
let completedFetchers = [];
|
|
1909
|
+
let deletedFetchersKeys = [];
|
|
1907
1910
|
if (future.v7_fetcherPersist) {
|
|
1908
1911
|
state.fetchers.forEach((fetcher, key) => {
|
|
1909
1912
|
if (fetcher.state === "idle") {
|
|
1910
1913
|
if (deletedFetchers.has(key)) {
|
|
1911
|
-
//
|
|
1912
|
-
|
|
1913
|
-
deleteFetcher(key);
|
|
1914
|
+
// Unmounted from the UI and can be totally removed
|
|
1915
|
+
deletedFetchersKeys.push(key);
|
|
1914
1916
|
} else {
|
|
1915
|
-
//
|
|
1916
|
-
//
|
|
1917
|
-
|
|
1918
|
-
state.fetchers.delete(key);
|
|
1917
|
+
// Returned to idle but still mounted in the UI, so semi-remains for
|
|
1918
|
+
// revalidations and such
|
|
1919
|
+
completedFetchers.push(key);
|
|
1919
1920
|
}
|
|
1920
1921
|
}
|
|
1921
1922
|
});
|
|
1922
1923
|
}
|
|
1924
|
+
subscribers.forEach(subscriber => subscriber(state, {
|
|
1925
|
+
deletedFetchers: deletedFetchersKeys,
|
|
1926
|
+
unstable_viewTransitionOpts: viewTransitionOpts
|
|
1927
|
+
}));
|
|
1928
|
+
|
|
1929
|
+
// Remove idle fetchers from state since we only care about in-flight fetchers.
|
|
1930
|
+
if (future.v7_fetcherPersist) {
|
|
1931
|
+
completedFetchers.forEach(key => state.fetchers.delete(key));
|
|
1932
|
+
deletedFetchersKeys.forEach(key => deleteFetcher(key));
|
|
1933
|
+
}
|
|
1923
1934
|
}
|
|
1924
1935
|
|
|
1925
1936
|
// Complete a navigation returning the state.navigation back to the IDLE_NAVIGATION
|
|
@@ -2472,6 +2483,14 @@ function createRouter(init) {
|
|
|
2472
2483
|
} : {});
|
|
2473
2484
|
}
|
|
2474
2485
|
function getFetcher(key) {
|
|
2486
|
+
if (future.v7_fetcherPersist) {
|
|
2487
|
+
activeFetchers.set(key, (activeFetchers.get(key) || 0) + 1);
|
|
2488
|
+
// If this fetcher was previously marked for deletion, unmark it since we
|
|
2489
|
+
// have a new instance
|
|
2490
|
+
if (deletedFetchers.has(key)) {
|
|
2491
|
+
deletedFetchers.delete(key);
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2475
2494
|
return state.fetchers.get(key) || IDLE_FETCHER;
|
|
2476
2495
|
}
|
|
2477
2496
|
|
|
@@ -2545,13 +2564,20 @@ function createRouter(init) {
|
|
|
2545
2564
|
let originatingLoadId = incrementingLoadId;
|
|
2546
2565
|
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, manifest, mapRouteProperties, basename);
|
|
2547
2566
|
if (fetchRequest.signal.aborted) {
|
|
2548
|
-
// We can delete this so long as we weren't aborted by
|
|
2567
|
+
// We can delete this so long as we weren't aborted by our own fetcher
|
|
2549
2568
|
// re-submit which would have put _new_ controller is in fetchControllers
|
|
2550
2569
|
if (fetchControllers.get(key) === abortController) {
|
|
2551
2570
|
fetchControllers.delete(key);
|
|
2552
2571
|
}
|
|
2553
2572
|
return;
|
|
2554
2573
|
}
|
|
2574
|
+
if (deletedFetchers.has(key)) {
|
|
2575
|
+
state.fetchers.set(key, getDoneFetcher(undefined));
|
|
2576
|
+
updateState({
|
|
2577
|
+
fetchers: new Map(state.fetchers)
|
|
2578
|
+
});
|
|
2579
|
+
return;
|
|
2580
|
+
}
|
|
2555
2581
|
if (isRedirectResult(actionResult)) {
|
|
2556
2582
|
fetchControllers.delete(key);
|
|
2557
2583
|
if (pendingNavigationLoadId > originatingLoadId) {
|
|
@@ -2721,6 +2747,13 @@ function createRouter(init) {
|
|
|
2721
2747
|
if (fetchRequest.signal.aborted) {
|
|
2722
2748
|
return;
|
|
2723
2749
|
}
|
|
2750
|
+
if (deletedFetchers.has(key)) {
|
|
2751
|
+
state.fetchers.set(key, getDoneFetcher(undefined));
|
|
2752
|
+
updateState({
|
|
2753
|
+
fetchers: new Map(state.fetchers)
|
|
2754
|
+
});
|
|
2755
|
+
return;
|
|
2756
|
+
}
|
|
2724
2757
|
|
|
2725
2758
|
// If the loader threw a redirect Response, start a new REPLACE navigation
|
|
2726
2759
|
if (isRedirectResult(result)) {
|
|
@@ -2742,17 +2775,7 @@ function createRouter(init) {
|
|
|
2742
2775
|
|
|
2743
2776
|
// Process any non-redirect errors thrown
|
|
2744
2777
|
if (isErrorResult(result)) {
|
|
2745
|
-
|
|
2746
|
-
state.fetchers.delete(key);
|
|
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
|
-
}
|
|
2755
|
-
});
|
|
2778
|
+
setFetcherError(key, routeId, result.error);
|
|
2756
2779
|
return;
|
|
2757
2780
|
}
|
|
2758
2781
|
invariant(!isDeferredResult(result), "Unhandled fetcher deferred data");
|
|
@@ -2929,7 +2952,13 @@ function createRouter(init) {
|
|
|
2929
2952
|
}
|
|
2930
2953
|
function deleteFetcherAndUpdateState(key) {
|
|
2931
2954
|
if (future.v7_fetcherPersist) {
|
|
2932
|
-
|
|
2955
|
+
let count = (activeFetchers.get(key) || 0) - 1;
|
|
2956
|
+
if (count <= 0) {
|
|
2957
|
+
activeFetchers.delete(key);
|
|
2958
|
+
deletedFetchers.add(key);
|
|
2959
|
+
} else {
|
|
2960
|
+
activeFetchers.set(key, count);
|
|
2961
|
+
}
|
|
2933
2962
|
} else {
|
|
2934
2963
|
deleteFetcher(key);
|
|
2935
2964
|
}
|