Skip to content

Commit 7c97778

Browse files
authoredFeb 28, 2024··
fix(suspense): ensure nested suspense patching if in fallback state (#10417)
close #10415
1 parent 1f6a110 commit 7c97778

File tree

2 files changed

+132
-13
lines changed

2 files changed

+132
-13
lines changed
 

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

+127-12
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ describe('Suspense', () => {
5454
}
5555
}
5656

57+
const RouterView = {
58+
setup(_: any, { slots }: any) {
59+
const route = inject('route') as any
60+
const depth = inject('depth', 0)
61+
provide('depth', depth + 1)
62+
return () => {
63+
const current = route.value[depth]
64+
return slots.default({ Component: current })[0]
65+
}
66+
},
67+
}
68+
5769
test('fallback content', async () => {
5870
const Async = defineAsyncComponent({
5971
render() {
@@ -1041,18 +1053,6 @@ describe('Suspense', () => {
10411053

10421054
// #10098
10431055
test('switching branches w/ nested suspense', async () => {
1044-
const RouterView = {
1045-
setup(_: any, { slots }: any) {
1046-
const route = inject('route') as any
1047-
const depth = inject('depth', 0)
1048-
provide('depth', depth + 1)
1049-
return () => {
1050-
const current = route.value[depth]
1051-
return slots.default({ Component: current })[0]
1052-
}
1053-
},
1054-
}
1055-
10561056
const OuterB = defineAsyncComponent({
10571057
setup: () => {
10581058
return () =>
@@ -1132,6 +1132,121 @@ describe('Suspense', () => {
11321132
expect(serializeInner(root)).toBe(`<div>innerA</div>`)
11331133
})
11341134

1135+
// #10415
1136+
test('nested suspense (w/ suspensible) switch several times before parent suspense resolve', async () => {
1137+
const OuterA = defineAsyncComponent({
1138+
setup: () => {
1139+
return () =>
1140+
h(RouterView, null, {
1141+
default: ({ Component }: any) => [
1142+
h(Suspense, null, {
1143+
default: () => h(Component),
1144+
}),
1145+
],
1146+
})
1147+
},
1148+
})
1149+
1150+
const InnerA = defineAsyncComponent({
1151+
setup: () => {
1152+
return () => h('div', 'innerA')
1153+
},
1154+
})
1155+
1156+
const route = shallowRef([OuterA, InnerA])
1157+
const InnerB = defineAsyncComponent(
1158+
{
1159+
setup: () => {
1160+
return () => h('div', 'innerB')
1161+
},
1162+
},
1163+
5,
1164+
)
1165+
1166+
const InnerB1 = defineAsyncComponent(
1167+
{
1168+
setup: () => {
1169+
return () => h('div', 'innerB1')
1170+
},
1171+
},
1172+
5,
1173+
)
1174+
1175+
const InnerB2 = defineAsyncComponent(
1176+
{
1177+
setup: () => {
1178+
return () => h('div', 'innerB2')
1179+
},
1180+
},
1181+
5,
1182+
)
1183+
1184+
const OuterB = defineAsyncComponent(
1185+
{
1186+
setup() {
1187+
nextTick(async () => {
1188+
await new Promise(resolve => setTimeout(resolve, 1))
1189+
route.value = [OuterB, InnerB1]
1190+
})
1191+
1192+
nextTick(async () => {
1193+
await new Promise(resolve => setTimeout(resolve, 1))
1194+
route.value = [OuterB, InnerB2]
1195+
})
1196+
1197+
return () =>
1198+
h(RouterView, null, {
1199+
default: ({ Component }: any) => [
1200+
h(
1201+
Suspense,
1202+
{ suspensible: true },
1203+
{
1204+
default: () => h(Component),
1205+
},
1206+
),
1207+
],
1208+
})
1209+
},
1210+
},
1211+
5,
1212+
)
1213+
1214+
const Comp = {
1215+
setup() {
1216+
provide('route', route)
1217+
return () =>
1218+
h(RouterView, null, {
1219+
default: ({ Component }: any) => [
1220+
h(Suspense, null, {
1221+
default: () => h(Component),
1222+
}),
1223+
],
1224+
})
1225+
},
1226+
}
1227+
1228+
const root = nodeOps.createElement('div')
1229+
render(h(Comp), root)
1230+
await Promise.all(deps)
1231+
await nextTick()
1232+
expect(serializeInner(root)).toBe(`<!---->`)
1233+
1234+
await Promise.all(deps)
1235+
await nextTick()
1236+
expect(serializeInner(root)).toBe(`<div>innerA</div>`)
1237+
1238+
deps.length = 0
1239+
1240+
route.value = [OuterB, InnerB]
1241+
await nextTick()
1242+
1243+
await Promise.all(deps)
1244+
await Promise.all(deps)
1245+
await Promise.all(deps)
1246+
await nextTick()
1247+
expect(serializeInner(root)).toBe(`<div>innerB2</div>`)
1248+
})
1249+
11351250
test('branch switch to 3rd branch before resolve', async () => {
11361251
const calls: string[] = []
11371252

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

+5-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,11 @@ export const SuspenseImpl = {
9999
// 2. mounting along with the pendingBranch of parentSuspense
100100
// it is necessary to skip the current patch to avoid multiple mounts
101101
// of inner components.
102-
if (parentSuspense && parentSuspense.deps > 0) {
102+
if (
103+
parentSuspense &&
104+
parentSuspense.deps > 0 &&
105+
!n1.suspense!.isInFallback
106+
) {
103107
n2.suspense = n1.suspense!
104108
n2.suspense.vnode = n2
105109
n2.el = n1.el

0 commit comments

Comments
 (0)
Please sign in to comment.