Skip to content

Commit 633bbe7

Browse files
authored
fix(react-router): scroll with revalidation (#13671)
1 parent 42575fd commit 633bbe7

File tree

5 files changed

+54
-6
lines changed

5 files changed

+54
-6
lines changed

.changeset/kind-paws-try.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
Skip scroll restoration on useRevalidator() calls because they're not new locations

contributors.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
- decadentsavant
9393
- developit
9494
- dgrijuela
95+
- DimaAmega
9596
- DigitalNaut
9697
- dmitrytarassov
9798
- dokeet

packages/react-router/__tests__/router/scroll-restoration-test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,46 @@ describe("scroll restoration", () => {
235235
expect(t.router.state.restoreScrollPosition).toBe(50);
236236
expect(t.router.state.preventScrollReset).toBe(false);
237237
});
238+
239+
it("does not restore scroll on revalidation", async () => {
240+
let t = setup({
241+
routes: SCROLL_ROUTES,
242+
initialEntries: ["/"],
243+
});
244+
245+
expect(t.router.state.restoreScrollPosition).toBe(null);
246+
expect(t.router.state.preventScrollReset).toBe(false);
247+
248+
let positions = {};
249+
250+
// Simulate scrolling to 100 on /
251+
let activeScrollPosition = 100;
252+
t.router.enableScrollRestoration(positions, () => activeScrollPosition);
253+
254+
// Revalidate
255+
let R = await t.revalidate();
256+
await R.loaders.index.resolve("INDEX");
257+
258+
expect(t.router.state.restoreScrollPosition).toBe(false);
259+
expect(t.router.state.preventScrollReset).toBe(false);
260+
261+
// Scroll to 200
262+
activeScrollPosition = 200;
263+
264+
// Go to /tasks
265+
let nav1 = await t.navigate("/tasks");
266+
await nav1.loaders.tasks.resolve("TASKS");
267+
268+
expect(t.router.state.restoreScrollPosition).toBe(null);
269+
expect(t.router.state.preventScrollReset).toBe(false);
270+
271+
// Restore on pop back to /
272+
let nav2 = await t.navigate(-1);
273+
expect(t.router.state.restoreScrollPosition).toBe(null);
274+
await nav2.loaders.index.resolve("INDEX");
275+
expect(t.router.state.restoreScrollPosition).toBe(200);
276+
expect(t.router.state.preventScrollReset).toBe(false);
277+
});
238278
});
239279

240280
describe("scroll reset", () => {

packages/react-router/lib/dom/lib.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2076,7 +2076,7 @@ export function useScrollRestoration({
20762076
// Restore scrolling when state.restoreScrollPosition changes
20772077
// eslint-disable-next-line react-hooks/rules-of-hooks
20782078
React.useLayoutEffect(() => {
2079-
// Explicit false means don't do anything (used for submissions)
2079+
// Explicit false means don't do anything (used for submissions or revalidations)
20802080
if (restoreScrollPosition === false) {
20812081
return;
20822082
}

packages/react-router/lib/router/router.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ export interface RouterState {
327327
/**
328328
* Current scroll position we should start at for a new view
329329
* - number -> scroll position to restore to
330-
* - false -> do not restore scroll at all (used during submissions)
330+
* - false -> do not restore scroll at all (used during submissions/revalidations)
331331
* - null -> don't have a saved position, scroll to hash or top of page
332332
*/
333333
restoreScrollPosition: number | false | null;
@@ -1310,6 +1310,11 @@ export function createRouter(init: RouterInit): Router {
13101310
blockers.forEach((_, k) => blockers.set(k, IDLE_BLOCKER));
13111311
}
13121312

1313+
// Don't restore on router.revalidate()
1314+
let restoreScrollPosition = isUninterruptedRevalidation ?
1315+
false :
1316+
getSavedScrollPosition(location, newState.matches || state.matches);
1317+
13131318
// Always respect the user flag. Otherwise don't reset on mutation
13141319
// submission navigations unless they redirect
13151320
let preventScrollReset =
@@ -1378,10 +1383,7 @@ export function createRouter(init: RouterInit): Router {
13781383
initialized: true,
13791384
navigation: IDLE_NAVIGATION,
13801385
revalidation: "idle",
1381-
restoreScrollPosition: getSavedScrollPosition(
1382-
location,
1383-
newState.matches || state.matches
1384-
),
1386+
restoreScrollPosition,
13851387
preventScrollReset,
13861388
blockers,
13871389
},

0 commit comments

Comments
 (0)