Skip to content

Commit b799598

Browse files
committed
fix(loaders): ensure loads when a navigation is missed
Fix #495
1 parent c9c792e commit b799598

File tree

3 files changed

+66
-1
lines changed

3 files changed

+66
-1
lines changed

src/data-loaders/defineColadaLoader.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,8 @@ export function defineColadaLoader<Data>(
363363
!entry ||
364364
// we are nested and the parent is loading a different route than us
365365
(parentEntry && entry.pendingTo !== route)
366+
// The user somehow rendered the page without a navigation
367+
|| !entry.pendingLoad
366368
) {
367369
// console.log(
368370
// `🔁 loading from useData for "${options.key}": "${route.fullPath}"`

src/data-loaders/defineLoader.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ export function defineBasicLoader<Data>(
302302
(parentEntry && entry.pendingTo !== route)
303303
// we could also check for: but that would break nested loaders since they need to be always called to be associated with the parent
304304
// && entry.to !== route
305+
// the user managed to render the router view after a valid navigation + a failed navigation
306+
// https://github.com/posva/unplugin-vue-router/issues/495
307+
|| !entry.pendingLoad
305308
) {
306309
// console.log(
307310
// `🔁 loading from useData for "${options.key}": "${route.fullPath}"`

tests/data-loaders/tester.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
/**
22
* @vitest-environment happy-dom
33
*/
4-
import { type App, defineComponent, inject, type Plugin } from 'vue'
4+
import {
5+
type App,
6+
defineComponent,
7+
h,
8+
inject,
9+
nextTick,
10+
type Plugin,
11+
ref,
12+
} from 'vue'
513
import { beforeEach, describe, expect, it, vi } from 'vitest'
614
import { flushPromises, mount } from '@vue/test-utils'
715
import { getRouter } from 'vue-router-mock'
@@ -1109,6 +1117,58 @@ export function testDefineLoader<Context = void>(
11091117
it.todo('can be first non-lazy then lazy', async () => {})
11101118
it.todo('can be first non-lazy then lazy', async () => {})
11111119

1120+
// https://github.com/posva/unplugin-vue-router/issues/495
1121+
// in the issue above we have one page with a loader
1122+
// this page is conditionally rendered based on an error state
1123+
// when resetting the error state, there is also a duplicated navigation
1124+
// that invalidates any pendingLoad and renders the page again
1125+
// since there is no navigation, loaders are not called again and
1126+
// there is no pendingLoad
1127+
it('gracefully handles a loader without a pendingLoad', async () => {
1128+
const l1 = mockedLoader({ lazy: false, key: 'l1' })
1129+
const router = getRouter()
1130+
router.addRoute({
1131+
name: 'a',
1132+
path: '/a',
1133+
component: defineComponent({
1134+
setup() {
1135+
const { data } = l1.loader()
1136+
return { data }
1137+
},
1138+
template: `<p>{{ data }}</p>`,
1139+
}),
1140+
meta: {
1141+
loaders: [l1.loader],
1142+
},
1143+
})
1144+
l1.spy.mockResolvedValue('ok')
1145+
1146+
const isVisible = ref(true)
1147+
1148+
1149+
const wrapper = mount(
1150+
() => (isVisible.value ? h(RouterViewMock) : h('p', ['hidden'])),
1151+
{
1152+
global: {
1153+
plugins: [
1154+
[DataLoaderPlugin, { router }],
1155+
...(plugins?.(customContext!) || []),
1156+
],
1157+
},
1158+
}
1159+
)
1160+
1161+
await router.push('/a')
1162+
expect(wrapper.text()).toBe('ok')
1163+
isVisible.value = false
1164+
await nextTick()
1165+
expect(wrapper.text()).toBe('hidden')
1166+
await router.push('/a') // failed duplicated navigation
1167+
isVisible.value = true
1168+
await nextTick()
1169+
expect(wrapper.text()).toBe('ok')
1170+
})
1171+
11121172
describe('app.runWithContext()', () => {
11131173
it('can inject globals', async () => {
11141174
const { router, useData, app } = singleLoaderOneRoute(

0 commit comments

Comments
 (0)