Skip to content

Commit cf7f1db

Browse files
committed
fix(suspense): fix nested async child toggle inside already resovled suspense
fix #2215
1 parent 426a6c9 commit cf7f1db

File tree

2 files changed

+37
-6
lines changed

2 files changed

+37
-6
lines changed

packages/runtime-core/__tests__/components/Suspense.spec.ts

+32
Original file line numberDiff line numberDiff line change
@@ -1093,4 +1093,36 @@ describe('Suspense', () => {
10931093
await nextTick()
10941094
expect(root.innerHTML).toBe(`<div><span>2</span></div>`)
10951095
})
1096+
1097+
// #2215
1098+
test('toggling nested async setup component inside already resolved suspense', async () => {
1099+
const toggle = ref(false)
1100+
const Child = {
1101+
async setup() {
1102+
return () => h('div', 'child')
1103+
}
1104+
}
1105+
const Parent = () => h('div', ['parent', toggle.value ? h(Child) : null])
1106+
const Comp = {
1107+
setup() {
1108+
return () => h(Suspense, () => h(Parent))
1109+
}
1110+
}
1111+
1112+
const root = nodeOps.createElement('div')
1113+
render(h(Comp), root)
1114+
expect(serializeInner(root)).toBe(`<div>parent<!----></div>`)
1115+
1116+
toggle.value = true
1117+
// wait for flush
1118+
await nextTick()
1119+
// wait for child async setup resolve
1120+
await nextTick()
1121+
// child should be rendered now instead of stuck in limbo
1122+
expect(serializeInner(root)).toBe(`<div>parent<div>child</div></div>`)
1123+
1124+
toggle.value = false
1125+
await nextTick()
1126+
expect(serializeInner(root)).toBe(`<div>parent<!----></div>`)
1127+
})
10961128
})

packages/runtime-core/src/components/Suspense.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -542,12 +542,11 @@ function createSuspenseBoundary(
542542
},
543543

544544
registerDep(instance, setupRenderEffect) {
545-
if (!suspense.pendingBranch) {
546-
return
545+
const isInPendingSuspense = !!suspense.pendingBranch
546+
if (isInPendingSuspense) {
547+
suspense.deps++
547548
}
548-
549549
const hydratedEl = instance.vnode.el
550-
suspense.deps++
551550
instance
552551
.asyncDep!.catch(err => {
553552
handleError(err, instance, ErrorCodes.SETUP_FUNCTION)
@@ -562,7 +561,6 @@ function createSuspenseBoundary(
562561
) {
563562
return
564563
}
565-
suspense.deps--
566564
// retry from this component
567565
instance.asyncResolved = true
568566
const { vnode } = instance
@@ -597,7 +595,8 @@ function createSuspenseBoundary(
597595
if (__DEV__) {
598596
popWarningContext()
599597
}
600-
if (suspense.deps === 0) {
598+
// only decrease deps count if suspense is not already resolved
599+
if (isInPendingSuspense && --suspense.deps === 0) {
601600
suspense.resolve()
602601
}
603602
})

0 commit comments

Comments
 (0)