Skip to content

Commit 00de3e6

Browse files
authored
fix(Suspense): calling hooks before the transition finishes (#9388)
close #5844 close #5952
1 parent 7334376 commit 00de3e6

File tree

2 files changed

+94
-3
lines changed

2 files changed

+94
-3
lines changed

Diff for: packages/runtime-core/src/components/Suspense.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -491,17 +491,20 @@ function createSuspenseBoundary(
491491
container
492492
} = suspense
493493

494+
// if there's a transition happening we need to wait it to finish.
495+
let delayEnter: boolean | null = false
494496
if (suspense.isHydrating) {
495497
suspense.isHydrating = false
496498
} else if (!resume) {
497-
const delayEnter =
499+
delayEnter =
498500
activeBranch &&
499501
pendingBranch!.transition &&
500502
pendingBranch!.transition.mode === 'out-in'
501503
if (delayEnter) {
502504
activeBranch!.transition!.afterLeave = () => {
503505
if (pendingId === suspense.pendingId) {
504506
move(pendingBranch!, container, anchor, MoveType.ENTER)
507+
queuePostFlushCb(effects)
505508
}
506509
}
507510
}
@@ -538,8 +541,8 @@ function createSuspenseBoundary(
538541
}
539542
parent = parent.parent
540543
}
541-
// no pending parent suspense, flush all jobs
542-
if (!hasUnresolvedAncestor) {
544+
// no pending parent suspense nor transition, flush all jobs
545+
if (!hasUnresolvedAncestor && !delayEnter) {
543546
queuePostFlushCb(effects)
544547
}
545548
suspense.effects = []

Diff for: packages/vue/__tests__/e2e/Transition.spec.ts

+88
Original file line numberDiff line numberDiff line change
@@ -1498,6 +1498,94 @@ describe('e2e: Transition', () => {
14981498
},
14991499
E2E_TIMEOUT
15001500
)
1501+
1502+
// #5844
1503+
test('children mount should be called after html changes', async () => {
1504+
const fooMountSpy = vi.fn()
1505+
const barMountSpy = vi.fn()
1506+
1507+
await page().exposeFunction('fooMountSpy', fooMountSpy)
1508+
await page().exposeFunction('barMountSpy', barMountSpy)
1509+
1510+
await page().evaluate(() => {
1511+
const { fooMountSpy, barMountSpy } = window as any
1512+
const { createApp, ref, h, onMounted } = (window as any).Vue
1513+
createApp({
1514+
template: `
1515+
<div id="container">
1516+
<transition mode="out-in">
1517+
<Suspense>
1518+
<Foo v-if="toggle" />
1519+
<Bar v-else />
1520+
</Suspense>
1521+
</transition>
1522+
</div>
1523+
<button id="toggleBtn" @click="click">button</button>
1524+
`,
1525+
components: {
1526+
Foo: {
1527+
setup() {
1528+
const el = ref(null)
1529+
onMounted(() => {
1530+
fooMountSpy(
1531+
!!el.value,
1532+
!!document.getElementById('foo'),
1533+
!!document.getElementById('bar')
1534+
)
1535+
})
1536+
1537+
return () => h('div', { ref: el, id: 'foo' }, 'Foo')
1538+
}
1539+
},
1540+
Bar: {
1541+
setup() {
1542+
const el = ref(null)
1543+
onMounted(() => {
1544+
barMountSpy(
1545+
!!el.value,
1546+
!!document.getElementById('foo'),
1547+
!!document.getElementById('bar')
1548+
)
1549+
})
1550+
1551+
return () => h('div', { ref: el, id: 'bar' }, 'Bar')
1552+
}
1553+
}
1554+
},
1555+
setup: () => {
1556+
const toggle = ref(true)
1557+
const click = () => (toggle.value = !toggle.value)
1558+
return { toggle, click }
1559+
}
1560+
}).mount('#app')
1561+
})
1562+
1563+
await nextFrame()
1564+
expect(await html('#container')).toBe('<div id="foo">Foo</div>')
1565+
await transitionFinish()
1566+
1567+
expect(fooMountSpy).toBeCalledTimes(1)
1568+
expect(fooMountSpy).toHaveBeenNthCalledWith(1, true, true, false)
1569+
1570+
await page().evaluate(async () => {
1571+
;(document.querySelector('#toggleBtn') as any)!.click()
1572+
// nextTrick for patch start
1573+
await Promise.resolve()
1574+
// nextTrick for Suspense resolve
1575+
await Promise.resolve()
1576+
// nextTrick for dom transition start
1577+
await Promise.resolve()
1578+
return document.querySelector('#container div')!.className.split(/\s+/g)
1579+
})
1580+
1581+
await nextFrame()
1582+
await transitionFinish()
1583+
1584+
expect(await html('#container')).toBe('<div id="bar" class="">Bar</div>')
1585+
1586+
expect(barMountSpy).toBeCalledTimes(1)
1587+
expect(barMountSpy).toHaveBeenNthCalledWith(1, true, false, true)
1588+
})
15011589
})
15021590

15031591
describe('transition with v-show', () => {

0 commit comments

Comments
 (0)