Skip to content

Commit 6e25dbf

Browse files
committed
wait for all loaders before navigation
1 parent 1eaa08d commit 6e25dbf

2 files changed

Lines changed: 20 additions & 6 deletions

File tree

e2e/qwik-e2e/tests/qwikrouter/loaders.e2e.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,6 @@ test.describe('loaders', () => {
5252
await expect(title).toHaveText('Loaders - Qwik', { useInnerText: true });
5353
await expect(date).toHaveText('date: 2021-01-01T00:00:00.000Z');
5454
await expect(slow).toHaveText('slow: 123');
55-
// SPA navigation shows stale values first, then updates asynchronously via re-fetch
56-
if (javaScriptEnabled) {
57-
await expect(nestedName).toHaveText('name: Manuel');
58-
}
5955
await expect(nestedName).toHaveText('name: stuff');
6056
await expect(nestedDate).toHaveText('date: 2021-01-01T00:00:00.000Z');
6157
await expect(nestedDep).toHaveText('dep: 84');

packages/qwik-router/src/runtime/src/qwik-router-component.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ const preventNav: {
124124

125125
// Track navigations during prevent so we don't overwrite
126126
// We need to use an object so we can write into it from qrls
127-
const internalState = { navCount: 0 };
127+
const internalState = { navCount: 0, redirectCount: 0 };
128128

129129
/**
130130
* @public
@@ -512,7 +512,25 @@ export const useQwikRouter = (props?: QwikRouterProps) => {
512512
const contentModules = $mods$ as ContentModule[];
513513
// Update the loader context for the new route (triggers track subscriptions)
514514
updateRouteLoaderCtx(routeLoaderCtx, loadedRoute.$loaderPaths$, trackUrl);
515-
ensureRouteLoaderSignals(contentModules, loaderState, routeLoaderCtx);
515+
const routeLoaders = ensureRouteLoaderSignals(contentModules, loaderState, routeLoaderCtx);
516+
// Await all loader signals — promise() triggers $computeIfNeeded$ for
517+
// INVALID signals (from store change or newly created) and waits for completion
518+
const navCountBefore = internalState.navCount;
519+
if (!isServer && routeLoaders.length > 0) {
520+
await Promise.all(routeLoaders.map((loader) => loaderState[loader.__id]?.promise()));
521+
}
522+
523+
// If a loader triggered a redirect via goto() during computation,
524+
// navCount will have changed. Bail so the redirect navigation takes over.
525+
if (internalState.navCount !== navCountBefore) {
526+
if (++internalState.redirectCount > 20) {
527+
console.error('Too many redirects, aborting navigation');
528+
internalState.redirectCount = 0;
529+
return;
530+
}
531+
return;
532+
}
533+
internalState.redirectCount = 0;
516534

517535
// Update httpStatus for 404/error pages
518536
if ($notFound$) {

0 commit comments

Comments
 (0)