Skip to content

Commit aa03f3a

Browse files
committed
fix: loaders not being collected from async components for repeated navigations
1 parent e8f8dc4 commit aa03f3a

File tree

4 files changed

+41
-0
lines changed

4 files changed

+41
-0
lines changed

src/data-loaders/meta-extensions.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
ABORT_CONTROLLER_KEY,
99
NAVIGATION_RESULTS_KEY,
1010
IS_SSR_KEY,
11+
LOADER_SET_PROMISES_KEY,
1112
} from './symbols'
1213
import { type NavigationResult } from './navigation-guard'
1314

@@ -70,6 +71,12 @@ declare module 'vue-router' {
7071
*/
7172
[LOADER_SET_KEY]?: Set<UseDataLoader>
7273

74+
/**
75+
* List of promises while loaders from async components are being collected.
76+
* @internal
77+
*/
78+
[LOADER_SET_PROMISES_KEY]?: Promise<void>[]
79+
7380
/**
7481
* The signal that is aborted when the navigation is canceled or an error occurs.
7582
* @internal

src/data-loaders/navigation-guard.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,26 @@ describe('navigation-guard', () => {
178178
expect([...set!]).toEqual([useDataOne, useDataTwo])
179179
})
180180

181+
it('collects all loaders from lazy loaded pages with repeated navigation', async () => {
182+
setupApp({ isSSR: false })
183+
const router = getRouter()
184+
router.addRoute({
185+
name: '_test',
186+
path: '/fetch',
187+
component: () =>
188+
import('../../tests/data-loaders/ComponentWithLoader.vue'),
189+
})
190+
191+
void router.push('/fetch')
192+
193+
// simulate repeated navigation while the async component is loading
194+
await Promise.resolve()
195+
await router.push('/fetch')
196+
197+
const set = router.currentRoute.value.meta[LOADER_SET_KEY]
198+
expect([...set!]).toEqual([useDataOne, useDataTwo])
199+
})
200+
181201
it('awaits for all loaders to be resolved', async () => {
182202
setupApp({ isSSR: false })
183203
const router = getRouter()

src/data-loaders/navigation-guard.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
IS_SSR_KEY,
1515
LOADER_ENTRIES_KEY,
1616
LOADER_SET_KEY,
17+
LOADER_SET_PROMISES_KEY,
1718
NAVIGATION_RESULTS_KEY,
1819
PENDING_LOCATION_KEY,
1920
} from './meta-extensions'
@@ -145,8 +146,14 @@ export function setupLoaderGuard({
145146
}
146147
})
147148

149+
record.meta[LOADER_SET_PROMISES_KEY] ??= []
150+
record.meta[LOADER_SET_PROMISES_KEY].push(promise)
148151
lazyLoadingPromises.push(promise)
149152
}
153+
} else if (record.meta[LOADER_SET_PROMISES_KEY]) {
154+
// When repeated navigation happen on the same route, loaders might still be
155+
// loading from async components, so we need to wait for them to resolve.
156+
lazyLoadingPromises.push(...record.meta[LOADER_SET_PROMISES_KEY])
150157
}
151158
}
152159

@@ -156,6 +163,7 @@ export function setupLoaderGuard({
156163
// merge the whole set of loaders
157164
for (const loader of record.meta[LOADER_SET_KEY]!) {
158165
to.meta[LOADER_SET_KEY]!.add(loader)
166+
to.meta[LOADER_SET_PROMISES_KEY] = undefined
159167
}
160168
}
161169
// we return nothing to remove the value to allow the navigation

src/data-loaders/symbols.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
*/
55
export const LOADER_SET_KEY = Symbol('loaders')
66

7+
/**
8+
* Retrieves promises for loaders which are still being collected.
9+
* @internal
10+
*/
11+
export const LOADER_SET_PROMISES_KEY = Symbol('loadersPromise')
12+
713
/**
814
* Retrieves the internal version of loader entries.
915
* @internal

0 commit comments

Comments
 (0)