Skip to content

Commit

Permalink
Do not revalidate unmounted fetchers when v7_persistFetcher is enabled (
Browse files Browse the repository at this point in the history
  • Loading branch information
brophdawg11 committed Nov 20, 2023
1 parent f320378 commit 5f530a7
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/unmounted-fetcher-revalidate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/router": patch
---

Do not revalidate unmounted fetchers when `v7_fetcherPersist` is enabled
101 changes: 101 additions & 0 deletions packages/react-router-dom/__tests__/data-browser-router-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6288,6 +6288,107 @@ function testDomRouter(
await waitFor(() => screen.getByText("FETCH ERROR"));
expect(getHtml(container)).toMatch("Unexpected Application Error!");
});

it("unmounted fetchers should not revalidate", async () => {
let count = 0;
let loaderDfd = createDeferred();
let actionDfd = createDeferred();
let router = createTestRouter(
[
{
path: "/",
action: () => actionDfd.promise,
Component() {
let [showFetcher, setShowFetcher] = React.useState(true);
let [fetcherData, setFetcherData] = React.useState(null);
let fetchers = useFetchers();
let actionData = useActionData();
let navigation = useNavigation();

return (
<>
<Form method="post">
<button type="submit">Submit Form</button>
<p>{`Navigation State: ${navigation.state}`}</p>
<p>{`Action Data: ${actionData}`}</p>
<p>{`Active Fetchers: ${fetchers.length}`}</p>
</Form>
{showFetcher ? (
<FetcherComponent
onClose={(data) => {
setFetcherData(data);
setShowFetcher(false);
}}
/>
) : (
<p>{fetcherData}</p>
)}
</>
);
},
},
{
path: "/fetch",
async loader() {
count++;
if (count === 1) return await loaderDfd.promise;
throw new Error("Fetcher load called too many times");
},
},
],
{ window: getWindow("/"), future: { v7_fetcherPersist: true } }
);

function FetcherComponent({ onClose }) {
let fetcher = useFetcher();

React.useEffect(() => {
if (fetcher.state === "idle" && fetcher.data) {
onClose(fetcher.data);
}
}, [fetcher, onClose]);

return (
<>
<button onClick={() => fetcher.load("/fetch")}>
Load Fetcher
</button>
<pre>{`Fetcher State: ${fetcher.state}`}</pre>
</>
);
}

render(<RouterProvider router={router} />);

fireEvent.click(screen.getByText("Load Fetcher"));
await waitFor(
() =>
screen.getByText("Active Fetchers: 1") &&
screen.getByText("Fetcher State: loading")
);

loaderDfd.resolve("FETCHER DATA");
await waitFor(
() =>
screen.getByText("FETCHER DATA") &&
screen.getByText("Active Fetchers: 0")
);

fireEvent.click(screen.getByText("Submit Form"));
await waitFor(() =>
screen.getByText("Navigation State: submitting")
);

actionDfd.resolve("ACTION");
await waitFor(
() =>
screen.getByText("Navigation State: idle") &&
screen.getByText("Active Fetchers: 0") &&
screen.getByText("Action Data: ACTION")
);

expect(count).toBe(1);
});
});
});

Expand Down
8 changes: 7 additions & 1 deletion packages/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1632,6 +1632,7 @@ export function createRouter(init: RouterInit): Router {
isRevalidationRequired,
cancelledDeferredRoutes,
cancelledFetcherLoads,
deletedFetchers,
fetchLoadMatches,
fetchRedirectIds,
routesToUse,
Expand Down Expand Up @@ -2005,6 +2006,7 @@ export function createRouter(init: RouterInit): Router {
isRevalidationRequired,
cancelledDeferredRoutes,
cancelledFetcherLoads,
deletedFetchers,
fetchLoadMatches,
fetchRedirectIds,
routesToUse,
Expand Down Expand Up @@ -3549,6 +3551,7 @@ function getMatchesToLoad(
isRevalidationRequired: boolean,
cancelledDeferredRoutes: string[],
cancelledFetcherLoads: string[],
deletedFetchers: Set<string>,
fetchLoadMatches: Map<string, FetchLoadMatch>,
fetchRedirectIds: Set<string>,
routesToUse: AgnosticDataRouteObject[],
Expand Down Expand Up @@ -3616,7 +3619,10 @@ function getMatchesToLoad(
let revalidatingFetchers: RevalidatingFetcher[] = [];
fetchLoadMatches.forEach((f, key) => {
// Don't revalidate if fetcher won't be present in the subsequent render
if (!matches.some((m) => m.route.id === f.routeId)) {
if (
!matches.some((m) => m.route.id === f.routeId) ||
deletedFetchers.has(key)
) {
return;
}

Expand Down

0 comments on commit 5f530a7

Please sign in to comment.