Skip to content

Commit 405f345

Browse files
authoredNov 6, 2023
fix(warn): avoid warning on empty children with Suspense (#3962)
1 parent b39fa1f commit 405f345

File tree

3 files changed

+81
-1
lines changed

3 files changed

+81
-1
lines changed
 

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

+74
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ import {
1717
onUnmounted,
1818
onErrorCaptured,
1919
shallowRef,
20+
SuspenseProps,
21+
resolveDynamicComponent,
2022
Fragment
2123
} from '@vue/runtime-test'
2224
import { createApp, defineComponent } from 'vue'
25+
import { type RawSlots } from 'packages/runtime-core/src/componentSlots'
2326

2427
describe('Suspense', () => {
2528
const deps: Promise<any>[] = []
@@ -1523,4 +1526,75 @@ describe('Suspense', () => {
15231526
expected = `<div>outerB</div><div>innerB</div>`
15241527
expect(serializeInner(root)).toBe(expected)
15251528
})
1529+
1530+
describe('warnings', () => {
1531+
// base function to check if a combination of slots warns or not
1532+
function baseCheckWarn(
1533+
shouldWarn: boolean,
1534+
children: RawSlots,
1535+
props: SuspenseProps | null = null
1536+
) {
1537+
const Comp = {
1538+
setup() {
1539+
return () => h(Suspense, props, children)
1540+
}
1541+
}
1542+
1543+
const root = nodeOps.createElement('div')
1544+
render(h(Comp), root)
1545+
1546+
if (shouldWarn) {
1547+
expect(`<Suspense> slots expect a single root node.`).toHaveBeenWarned()
1548+
} else {
1549+
expect(
1550+
`<Suspense> slots expect a single root node.`
1551+
).not.toHaveBeenWarned()
1552+
}
1553+
}
1554+
1555+
// actual function that we use in tests
1556+
const checkWarn = baseCheckWarn.bind(null, true)
1557+
const checkNoWarn = baseCheckWarn.bind(null, false)
1558+
1559+
test('does not warn on single child', async () => {
1560+
checkNoWarn({
1561+
default: h('div'),
1562+
fallback: h('div')
1563+
})
1564+
})
1565+
1566+
test('does not warn on null', async () => {
1567+
checkNoWarn({
1568+
default: null,
1569+
fallback: null
1570+
})
1571+
})
1572+
1573+
test('does not warn on <component :is="null" />', async () => {
1574+
checkNoWarn({
1575+
default: () => [resolveDynamicComponent(null)],
1576+
fallback: () => null
1577+
})
1578+
})
1579+
1580+
test('does not warn on empty array', async () => {
1581+
checkNoWarn({
1582+
default: [],
1583+
fallback: () => []
1584+
})
1585+
})
1586+
1587+
test('warns on multiple children in default', async () => {
1588+
checkWarn({
1589+
default: [h('div'), h('div')]
1590+
})
1591+
})
1592+
1593+
test('warns on multiple children in fallback', async () => {
1594+
checkWarn({
1595+
default: h('div'),
1596+
fallback: [h('div'), h('div')]
1597+
})
1598+
})
1599+
})
15261600
})

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
assertNumber
3030
} from '../warning'
3131
import { handleError, ErrorCodes } from '../errorHandling'
32+
import { NULL_DYNAMIC_COMPONENT } from '../helpers/resolveAssets'
3233

3334
export interface SuspenseProps {
3435
onResolve?: () => void
@@ -795,7 +796,11 @@ function normalizeSuspenseSlot(s: any) {
795796
}
796797
if (isArray(s)) {
797798
const singleChild = filterSingleRoot(s)
798-
if (__DEV__ && !singleChild) {
799+
if (
800+
__DEV__ &&
801+
!singleChild &&
802+
s.filter(child => child !== NULL_DYNAMIC_COMPONENT).length > 0
803+
) {
799804
warn(`<Suspense> slots expect a single root node.`)
800805
}
801806
s = singleChild

‎packages/runtime-core/src/vnode.ts

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ export type VNodeProps = {
114114

115115
type VNodeChildAtom =
116116
| VNode
117+
| typeof NULL_DYNAMIC_COMPONENT
117118
| string
118119
| number
119120
| boolean

0 commit comments

Comments
 (0)
Please sign in to comment.