Skip to content

Commit 8de0372

Browse files
committed
fix(suspense): display current branch if no fallback is provided
Fix #3986
1 parent ab6e927 commit 8de0372

File tree

2 files changed

+90
-2
lines changed

2 files changed

+90
-2
lines changed

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

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,32 @@ describe('Suspense', () => {
6969
expect(serializeInner(root)).toBe(`<div>async</div>`)
7070
})
7171

72+
test('fallback content', async () => {
73+
const Async = defineAsyncComponent({
74+
render() {
75+
return h('div', 'async')
76+
}
77+
})
78+
79+
const Comp = {
80+
setup() {
81+
return () =>
82+
h(Suspense, null, {
83+
default: h(Async),
84+
fallback: h('div', 'fallback')
85+
})
86+
}
87+
}
88+
89+
const root = nodeOps.createElement('div')
90+
render(h(Comp), root)
91+
expect(serializeInner(root)).toBe(`<div>fallback</div>`)
92+
93+
await Promise.all(deps)
94+
await nextTick()
95+
expect(serializeInner(root)).toBe(`<div>async</div>`)
96+
})
97+
7298
test('emits events', async () => {
7399
const Async = defineAsyncComponent({
74100
render() {
@@ -709,7 +735,7 @@ describe('Suspense', () => {
709735
<div v-if="errorMessage">{{ errorMessage }}</div>
710736
<Suspense v-else>
711737
<div>
712-
<Async />
738+
<Async />
713739
</div>
714740
<template #fallback>
715741
<div>fallback</div>
@@ -983,6 +1009,66 @@ describe('Suspense', () => {
9831009
expect(serializeInner(root)).toBe(`<div>foo<div>foo nested</div></div>`)
9841010
})
9851011

1012+
test('display previous branch when timeout + no fallback slot is provided', async () => {
1013+
const toggle = ref(false)
1014+
let resolve = () => {}
1015+
let promise: Promise<void>
1016+
function createPromise() {
1017+
promise = new Promise<void>(r => {
1018+
resolve = r
1019+
})
1020+
1021+
return promise
1022+
}
1023+
1024+
const Foo = {
1025+
async setup() {
1026+
await createPromise()
1027+
return () => h('div', ['foo'])
1028+
}
1029+
}
1030+
1031+
const onPending = jest.fn()
1032+
const onFallback = jest.fn()
1033+
const onResolve = jest.fn()
1034+
1035+
const Comp = {
1036+
setup() {
1037+
return () =>
1038+
h(
1039+
Suspense,
1040+
{ timeout: 0, onPending, onFallback, onResolve },
1041+
{
1042+
default: toggle.value ? h(Foo) : 'other'
1043+
}
1044+
)
1045+
}
1046+
}
1047+
1048+
const root = nodeOps.createElement('div')
1049+
render(h(Comp), root)
1050+
expect(serializeInner(root)).toBe(`other`)
1051+
expect(onPending).toHaveBeenCalledTimes(0)
1052+
expect(onFallback).toHaveBeenCalledTimes(0)
1053+
expect(onResolve).toHaveBeenCalledTimes(1)
1054+
1055+
toggle.value = true
1056+
await nextTick()
1057+
expect(serializeInner(root)).toBe(`other`)
1058+
expect(onPending).toHaveBeenCalledTimes(1)
1059+
expect(onFallback).toHaveBeenCalledTimes(0)
1060+
expect(onResolve).toHaveBeenCalledTimes(1)
1061+
1062+
resolve()
1063+
await promise!
1064+
await nextTick()
1065+
await nextTick()
1066+
expect(serializeInner(root)).toBe(`<div>foo</div>`)
1067+
expect(onPending).toHaveBeenCalledTimes(1)
1068+
expect(onFallback).toHaveBeenCalledTimes(0)
1069+
expect(onResolve).toHaveBeenCalledTimes(2)
1070+
})
1071+
9861072
test('branch switch to 3rd branch before resolve', async () => {
9871073
const calls: string[] = []
9881074

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
VNodeProps,
55
isSameVNodeType,
66
openBlock,
7+
Comment,
78
closeBlock,
89
currentBlock,
910
createVNode
@@ -516,7 +517,8 @@ function createSuspenseBoundary(
516517
},
517518

518519
fallback(fallbackVNode) {
519-
if (!suspense.pendingBranch) {
520+
// avoid displaying the fallback/emitting node if there isn't any
521+
if (!suspense.pendingBranch || fallbackVNode.type === Comment) {
520522
return
521523
}
522524

0 commit comments

Comments
 (0)