Просмотр исходного кода

fix(suspense): fix nested async child toggle inside already resovled suspense

fix #2215
Evan You 5 лет назад
Родитель
Сommit
cf7f1dbc9b

+ 32 - 0
packages/runtime-core/__tests__/components/Suspense.spec.ts

@@ -1093,4 +1093,36 @@ describe('Suspense', () => {
     await nextTick()
     expect(root.innerHTML).toBe(`<div><span>2</span></div>`)
   })
+
+  // #2215
+  test('toggling nested async setup component inside already resolved suspense', async () => {
+    const toggle = ref(false)
+    const Child = {
+      async setup() {
+        return () => h('div', 'child')
+      }
+    }
+    const Parent = () => h('div', ['parent', toggle.value ? h(Child) : null])
+    const Comp = {
+      setup() {
+        return () => h(Suspense, () => h(Parent))
+      }
+    }
+
+    const root = nodeOps.createElement('div')
+    render(h(Comp), root)
+    expect(serializeInner(root)).toBe(`<div>parent<!----></div>`)
+
+    toggle.value = true
+    // wait for flush
+    await nextTick()
+    // wait for child async setup resolve
+    await nextTick()
+    // child should be rendered now instead of stuck in limbo
+    expect(serializeInner(root)).toBe(`<div>parent<div>child</div></div>`)
+
+    toggle.value = false
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>parent<!----></div>`)
+  })
 })

+ 5 - 6
packages/runtime-core/src/components/Suspense.ts

@@ -542,12 +542,11 @@ function createSuspenseBoundary(
     },
 
     registerDep(instance, setupRenderEffect) {
-      if (!suspense.pendingBranch) {
-        return
+      const isInPendingSuspense = !!suspense.pendingBranch
+      if (isInPendingSuspense) {
+        suspense.deps++
       }
-
       const hydratedEl = instance.vnode.el
-      suspense.deps++
       instance
         .asyncDep!.catch(err => {
           handleError(err, instance, ErrorCodes.SETUP_FUNCTION)
@@ -562,7 +561,6 @@ function createSuspenseBoundary(
           ) {
             return
           }
-          suspense.deps--
           // retry from this component
           instance.asyncResolved = true
           const { vnode } = instance
@@ -597,7 +595,8 @@ function createSuspenseBoundary(
           if (__DEV__) {
             popWarningContext()
           }
-          if (suspense.deps === 0) {
+          // only decrease deps count if suspense is not already resolved
+          if (isInPendingSuspense && --suspense.deps === 0) {
             suspense.resolve()
           }
         })