Skip to content

Commit 03b86cc

Browse files
authoredMay 16, 2022
fix(remix-react): avoid duplicate loader calls when using prefetch-intent (#2938)
* fix: avoid duplicate loader call on prefetch intent * chore: fix typo in comment * fix: remove prefetch intent on mouse leave * docs: update docs on prefetch caching
1 parent a114c40 commit 03b86cc

File tree

3 files changed

+29
-6
lines changed

3 files changed

+29
-6
lines changed
 

‎docs/api/remix.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ In our effort to remove all loading states from your UI, `Link` can automaticall
125125
```
126126

127127
- **"none"** - Default behavior. This will prevent any prefetching from happening. This is recommended when linking to pages that require a user session that the browser won't be able to prefetch anyway.
128-
- **"intent"** - Recommended if you want to prefetch. Fetches when Remix thinks the user intends to visit the link. Right now the behavior is simple: if they hover or focus the link it will prefetch the resources. In the future we hope to make this even smarter. Links with large click areas/padding get a bit of a head start.
128+
- **"intent"** - Recommended if you want to prefetch. Fetches when Remix thinks the user intends to visit the link. Right now the behavior is simple: if they hover or focus the link it will prefetch the resources. In the future we hope to make this even smarter. Links with large click areas/padding get a bit of a head start. It is worth noting that when using `prefetch="intent"`, `<link rel="prefetch">` elements will be inserted on hover/focus and removed if the `<Link>` loses hover/focus. Without proper `cache-control` headers on your loaders this could result in repeated prefetch loads if a user continually hovers on and off a link.
129129
- **"render"** - Fetches when the link is rendered.
130130

131131
<docs-error>You may need to use the <code>:last-of-type</code> selector instead of <code>:last-child</code> when styling child elements inside of your links</docs-error>

‎integration/prefetch-test.ts

+27-5
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ function fixtureFactory(mode: RemixLinkProps["prefetch"]) {
5252

5353
"app/routes/index.jsx": js`
5454
export default function() {
55-
return <h2>Index</h2>;
55+
return <h2 className="index">Index</h2>;
5656
}
5757
`,
5858

@@ -61,13 +61,13 @@ function fixtureFactory(mode: RemixLinkProps["prefetch"]) {
6161
return { message: 'data from the loader' };
6262
}
6363
export default function() {
64-
return <h2>With Loader</h2>;
64+
return <h2 className="with-loader">With Loader</h2>;
6565
}
6666
`,
6767

6868
"app/routes/without-loader.jsx": js`
6969
export default function() {
70-
return <h2>Without Loader</h2>;
70+
return <h2 className="without-loader">Without Loader</h2>;
7171
}
7272
`,
7373
},
@@ -185,7 +185,29 @@ test.describe("prefetch=intent (hover)", () => {
185185
"#nav link[rel='modulepreload'][href^='/build/routes/without-loader-']",
186186
{ state: "attached" }
187187
);
188-
expect(await page.locator("#nav link").count()).toBe(3);
188+
expect(await page.locator("#nav link").count()).toBe(1);
189+
});
190+
191+
test("removes prefetch tags after navigating to/from the page", async ({
192+
page,
193+
}) => {
194+
let app = new PlaywrightFixture(appFixture, page);
195+
await app.goto("/");
196+
197+
// Links added on hover
198+
await page.hover("a[href='/with-loader']");
199+
await page.waitForSelector("#nav link", { state: "attached" });
200+
expect(await page.locator("#nav link").count()).toBe(2);
201+
202+
// Links removed upon navigating to the page
203+
await page.click("a[href='/with-loader']");
204+
await page.waitForSelector("h2.with-loader", { state: "attached" });
205+
expect(await page.locator("#nav link").count()).toBe(0);
206+
207+
// Links stay removed upon navigating away from the page
208+
await page.click("a[href='/without-loader']");
209+
await page.waitForSelector("h2.without-loader", { state: "attached" });
210+
expect(await page.locator("#nav link").count()).toBe(0);
189211
});
190212
});
191213

@@ -237,6 +259,6 @@ test.describe("prefetch=intent (focus)", () => {
237259
"#nav link[rel='modulepreload'][href^='/build/routes/without-loader-']",
238260
{ state: "attached" }
239261
);
240-
expect(await page.locator("#nav link").count()).toBe(3);
262+
expect(await page.locator("#nav link").count()).toBe(1);
241263
});
242264
});

‎packages/remix-react/components.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ function usePrefetchBehavior(
420420
let cancelIntent = () => {
421421
if (prefetch === "intent") {
422422
setMaybePrefetch(false);
423+
setShouldPrefetch(false);
423424
}
424425
};
425426

0 commit comments

Comments
 (0)
Please sign in to comment.