Skip to content

Commit a871315

Browse files
authored
fix(suspense): don't immediately resolve suspense on last dep unmount (#13456)
close #13453
1 parent 0562548 commit a871315

File tree

2 files changed

+76
-18
lines changed

2 files changed

+76
-18
lines changed

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

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
onUnmounted,
1818
ref,
1919
render,
20+
renderList,
21+
renderSlot,
2022
resolveDynamicComponent,
2123
serializeInner,
2224
shallowRef,
@@ -2161,6 +2163,80 @@ describe('Suspense', () => {
21612163
await Promise.all(deps)
21622164
})
21632165

2166+
// #13453
2167+
test('add new async deps during patching', async () => {
2168+
const getComponent = (type: string) => {
2169+
if (type === 'A') {
2170+
return defineAsyncComponent({
2171+
setup() {
2172+
return () => h('div', 'A')
2173+
},
2174+
})
2175+
}
2176+
return defineAsyncComponent({
2177+
setup() {
2178+
return () => h('div', 'B')
2179+
},
2180+
})
2181+
}
2182+
2183+
const types = ref(['A'])
2184+
const add = async () => {
2185+
types.value.push('B')
2186+
}
2187+
2188+
const update = async () => {
2189+
// mount Suspense B
2190+
// [Suspense A] -> [Suspense A(pending), Suspense B(pending)]
2191+
await add()
2192+
// patch Suspense B (still pending)
2193+
// [Suspense A(pending), Suspense B(pending)] -> [Suspense B(pending)]
2194+
types.value.shift()
2195+
}
2196+
2197+
const Comp = {
2198+
render(this: any) {
2199+
return h(Fragment, null, [
2200+
renderList(types.value, type => {
2201+
return h(
2202+
Suspense,
2203+
{ key: type },
2204+
{
2205+
default: () => [
2206+
renderSlot(this.$slots, 'default', { type: type }),
2207+
],
2208+
},
2209+
)
2210+
}),
2211+
])
2212+
},
2213+
}
2214+
2215+
const App = {
2216+
setup() {
2217+
return () =>
2218+
h(Comp, null, {
2219+
default: (params: any) => [h(getComponent(params.type))],
2220+
})
2221+
},
2222+
}
2223+
2224+
const root = nodeOps.createElement('div')
2225+
render(h(App), root)
2226+
expect(serializeInner(root)).toBe(`<!---->`)
2227+
2228+
await Promise.all(deps)
2229+
expect(serializeInner(root)).toBe(`<div>A</div>`)
2230+
2231+
update()
2232+
await nextTick()
2233+
// wait for both A and B to resolve
2234+
await Promise.all(deps)
2235+
// wait for new B to resolve
2236+
await Promise.all(deps)
2237+
expect(serializeInner(root)).toBe(`<div>B</div>`)
2238+
})
2239+
21642240
describe('warnings', () => {
21652241
// base function to check if a combination of slots warns or not
21662242
function baseCheckWarn(

packages/runtime-core/src/renderer.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2326,24 +2326,6 @@ function baseCreateRenderer(
23262326
instance.isUnmounted = true
23272327
}, parentSuspense)
23282328

2329-
// A component with async dep inside a pending suspense is unmounted before
2330-
// its async dep resolves. This should remove the dep from the suspense, and
2331-
// cause the suspense to resolve immediately if that was the last dep.
2332-
if (
2333-
__FEATURE_SUSPENSE__ &&
2334-
parentSuspense &&
2335-
parentSuspense.pendingBranch &&
2336-
!parentSuspense.isUnmounted &&
2337-
instance.asyncDep &&
2338-
!instance.asyncResolved &&
2339-
instance.suspenseId === parentSuspense.pendingId
2340-
) {
2341-
parentSuspense.deps--
2342-
if (parentSuspense.deps === 0) {
2343-
parentSuspense.resolve()
2344-
}
2345-
}
2346-
23472329
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
23482330
devtoolsComponentRemoved(instance)
23492331
}

0 commit comments

Comments
 (0)