Skip to content

Commit 1fe9c8a

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

File tree

4 files changed

+47
-0
lines changed

4 files changed

+47
-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: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
NavigationResult,
2626
DataLoaderPluginOptions,
2727
useIsDataLoading,
28+
LOADER_SET_PROMISES_KEY,
2829
} from 'unplugin-vue-router/data-loaders'
2930
import { mockPromise } from '../../tests/utils'
3031
import {
@@ -178,6 +179,30 @@ describe('navigation-guard', () => {
178179
expect([...set!]).toEqual([useDataOne, useDataTwo])
179180
})
180181

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

src/data-loaders/navigation-guard.ts

Lines changed: 9 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,15 @@ 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+
console.log('REUSE', record.path)
155+
// When repeated navigation happen on the same route, loaders might still be
156+
// loading from async components, so we need to wait for them to resolve.
157+
lazyLoadingPromises.push(...record.meta[LOADER_SET_PROMISES_KEY])
150158
}
151159
}
152160

@@ -157,6 +165,7 @@ export function setupLoaderGuard({
157165
for (const loader of record.meta[LOADER_SET_KEY]!) {
158166
to.meta[LOADER_SET_KEY]!.add(loader)
159167
}
168+
record.meta[LOADER_SET_PROMISES_KEY] = undefined
160169
}
161170
// we return nothing to remove the value to allow the navigation
162171
// same as return true

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)