Skip to content

Commit 5651176

Browse files
brophdawg11Artur-
andauthored
fix: Wait for restore url navigation to complete before proceeding (#11930)
Co-authored-by: Artur Signell <[email protected]>
1 parent ae99a1c commit 5651176

File tree

5 files changed

+33
-13
lines changed

5 files changed

+33
-13
lines changed

.changeset/serious-news-kick.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@remix-run/router": patch
3+
---
4+
5+
Fix blocker usage when `blocker.proceed` is called quickly/syncronously

contributors.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
- Armanio
2727
- arnassavickas
2828
- aroyan
29+
- Artur-
2930
- ashusnapx
3031
- avipatel97
3132
- awreese

packages/react-router-dom/__tests__/use-blocker-test.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ import {
1515

1616
type Router = ReturnType<typeof createMemoryRouter>;
1717

18-
const LOADER_LATENCY_MS = 100;
18+
const LOADER_LATENCY_MS = 200;
1919

2020
async function slowLoader() {
21-
await sleep(LOADER_LATENCY_MS);
21+
await sleep(LOADER_LATENCY_MS / 2);
2222
return json(null);
2323
}
2424

@@ -1084,14 +1084,23 @@ describe("navigation blocking with useBlocker", () => {
10841084
act(() => {
10851085
click(node.querySelector("[data-action='back']"));
10861086
});
1087-
act(() => {
1087+
expect(node.innerHTML).toContain("<h1>Contact</h1>");
1088+
await act(async () => {
10881089
click(node.querySelector("[data-action='proceed']"));
1090+
expect([...router.state.blockers.values()][0]).toEqual({
1091+
state: "proceeding",
1092+
proceed: undefined,
1093+
reset: undefined,
1094+
location: expect.any(Object),
1095+
});
1096+
await sleep(LOADER_LATENCY_MS);
10891097
});
1098+
expect(node.innerHTML).toContain("<h1>About</h1>");
10901099
expect(blocker).toEqual({
1091-
state: "proceeding",
1100+
state: "unblocked",
10921101
proceed: undefined,
10931102
reset: undefined,
1094-
location: expect.any(Object),
1103+
location: undefined,
10951104
});
10961105
});
10971106

packages/router/__tests__/navigation-blocking-test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ describe("navigation blocking", () => {
442442
router.getBlocker("KEY", fn);
443443
await router.navigate(-1);
444444
router.getBlocker("KEY", fn).proceed?.();
445-
await sleep(LOADER_LATENCY_MS);
445+
await sleep(LOADER_LATENCY_MS + 10);
446446
expect(router.getBlocker("KEY", fn)).toEqual({
447447
state: "unblocked",
448448
proceed: undefined,
@@ -455,7 +455,7 @@ describe("navigation blocking", () => {
455455
router.getBlocker("KEY", fn);
456456
await router.navigate(-1);
457457
router.getBlocker("KEY", fn).proceed?.();
458-
await sleep(LOADER_LATENCY_MS);
458+
await sleep(LOADER_LATENCY_MS + 10);
459459
expect(router.state.location.pathname).toBe("/about");
460460
});
461461
});

packages/router/router.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,7 +1033,7 @@ export function createRouter(init: RouterInit): Router {
10331033

10341034
// Flag to ignore the next history update, so we can revert the URL change on
10351035
// a POP navigation that was blocked by the user without touching router state
1036-
let ignoreNextHistoryUpdate = false;
1036+
let unblockBlockerHistoryUpdate: (() => void) | undefined = undefined;
10371037

10381038
// Initialize the router, all side effects should be kicked off from here.
10391039
// Implemented as a Fluent API for ease of:
@@ -1045,8 +1045,9 @@ export function createRouter(init: RouterInit): Router {
10451045
({ action: historyAction, location, delta }) => {
10461046
// Ignore this event if it was just us resetting the URL from a
10471047
// blocked POP navigation
1048-
if (ignoreNextHistoryUpdate) {
1049-
ignoreNextHistoryUpdate = false;
1048+
if (unblockBlockerHistoryUpdate) {
1049+
unblockBlockerHistoryUpdate();
1050+
unblockBlockerHistoryUpdate = undefined;
10501051
return;
10511052
}
10521053

@@ -1068,7 +1069,9 @@ export function createRouter(init: RouterInit): Router {
10681069

10691070
if (blockerKey && delta != null) {
10701071
// Restore the URL to match the current UI, but don't update router state
1071-
ignoreNextHistoryUpdate = true;
1072+
let nextHistoryUpdatePromise = new Promise<void>((resolve) => {
1073+
unblockBlockerHistoryUpdate = resolve;
1074+
});
10721075
init.history.go(delta * -1);
10731076

10741077
// Put the blocker into a blocked state
@@ -1082,8 +1085,10 @@ export function createRouter(init: RouterInit): Router {
10821085
reset: undefined,
10831086
location,
10841087
});
1085-
// Re-do the same POP navigation we just blocked
1086-
init.history.go(delta);
1088+
// Re-do the same POP navigation we just blocked, after the url
1089+
// restoration is also complete. See:
1090+
// https://github.com/remix-run/react-router/issues/11613
1091+
nextHistoryUpdatePromise.then(() => init.history.go(delta));
10871092
},
10881093
reset() {
10891094
let blockers = new Map(state.blockers);

0 commit comments

Comments
 (0)