Skip to content

Commit c54c631

Browse files
vaadin-botArtur-
andauthored
fix: Run only one queued navigate() at a time (#22021) (#22025)
This fixes cases where the server sends multiple navigate() calls in the same response. There is currently no IT that would fail but this might very well resolve some edge/timing cases without ITs. This is needed also for React 19 where timing is slightly different and the current solution does not work. Co-authored-by: Artur <artur@vaadin.com>
1 parent 10b52a9 commit c54c631

File tree

1 file changed

+14
-3
lines changed
  • flow-server/src/main/resources/com/vaadin/flow/server/frontend

1 file changed

+14
-3
lines changed

flow-server/src/main/resources/com/vaadin/flow/server/frontend/Flow.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ type NavigateOpts = {
277277

278278
type NavigateFn = (to: string, callback: boolean, opts?: NavigateOptions) => void;
279279

280+
let navigateInProgress = false;
280281
/**
281282
* A hook providing the `navigate(path: string, opts?: NavigateOptions)` function
282283
* with React Router API that has more consistent history updates. Uses internal
@@ -291,6 +292,11 @@ function useQueuedNavigate(
291292
const [navigateQueueLength, setNavigateQueueLength] = useState(0);
292293

293294
const dequeueNavigation = useCallback(() => {
295+
if (navigateInProgress) {
296+
dequeueNavigationAfterCurrentTask();
297+
return;
298+
}
299+
294300
const navigateArgs = navigateQueue.shift();
295301
if (navigateArgs === undefined) {
296302
// Empty queue, do nothing.
@@ -303,14 +309,15 @@ function useQueuedNavigate(
303309
waitReference.current = undefined;
304310
}
305311
navigated.current = !navigateArgs.callback;
312+
navigateInProgress = true;
306313
navigate(navigateArgs.to, navigateArgs.opts);
307314
setNavigateQueueLength(navigateQueue.length);
308315
};
309316
blockingNavigate();
310317
}, [navigate, setNavigateQueueLength]);
311318

312319
const dequeueNavigationAfterCurrentTask = useCallback(() => {
313-
queueMicrotask(dequeueNavigation);
320+
setTimeout(dequeueNavigation, 0);
314321
}, [dequeueNavigation]);
315322

316323
const enqueueNavigation = useCallback(
@@ -499,6 +506,7 @@ function Flow() {
499506
if (navigated.current && !fromAnchor.current) {
500507
blocker.proceed();
501508
blockingPromise.resolve();
509+
navigateInProgress = false;
502510
return;
503511
}
504512
fromAnchor.current = false;
@@ -516,12 +524,14 @@ function Flow() {
516524
prevent() {
517525
blocker.reset();
518526
blockingPromise.resolve();
527+
navigateInProgress = false;
519528
navigated.current = false;
520529
},
521530
redirect,
522531
continue() {
523532
blocker.proceed();
524533
blockingPromise.resolve();
534+
navigateInProgress = false;
525535
}
526536
},
527537
router
@@ -545,16 +555,17 @@ function Flow() {
545555
containerRef.current.serverConnected = (cancel) => {
546556
if (cancel) {
547557
blocker.reset();
548-
blockingPromise.resolve();
549558
} else {
550559
blocker.proceed();
551-
blockingPromise.resolve();
552560
}
561+
blockingPromise.resolve();
562+
navigateInProgress = false;
553563
};
554564
} else {
555565
// permitted navigation: proceed with the blocker
556566
blocker.proceed();
557567
blockingPromise.resolve();
568+
navigateInProgress = false;
558569
}
559570
});
560571
}

0 commit comments

Comments
 (0)