Przeglądaj źródła

fix(runtime-core): properly handle async component update before resolve (#11619)

close #11617
linzhe 2 miesięcy temu
rodzic
commit
e71c26c03d

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

@@ -15,6 +15,7 @@ import {
   onErrorCaptured,
   onMounted,
   onUnmounted,
+  onUpdated,
   ref,
   render,
   renderList,
@@ -2164,6 +2165,81 @@ describe('Suspense', () => {
     await Promise.all(deps)
   })
 
+  //#11617
+  test('update async component before resolve then update again', async () => {
+    const arr: boolean[] = []
+    const Child = {
+      props: ['loading'],
+      async setup(props: any) {
+        onUpdated(() => {
+          arr.push(props.loading)
+        })
+        await 1
+        return () => {
+          const loading = props.loading
+          return h('div', null, loading ? '1' : '2')
+        }
+      },
+    }
+
+    const Parent = defineComponent({
+      setup() {
+        const loading = ref(false)
+        const delay = (delayInms: any) => {
+          return new Promise(resolve => setTimeout(resolve, delayInms))
+        }
+        onMounted(async () => {
+          loading.value = true
+          await delay(1000)
+          loading.value = false
+          await nextTick()
+          expect(arr).toEqual([true, false])
+        })
+        return () => {
+          return h(Child, { loading: loading.value })
+        }
+      },
+    })
+
+    const RouterView = {
+      props: {
+        name: { type: Object },
+      },
+      setup(props: any) {
+        return () => {
+          const name = props.name
+          return h(name)
+        }
+      },
+    }
+    const App = {
+      setup() {
+        const Dummy = {
+          setup() {
+            return () => {
+              return h('div', null, 'dummy')
+            }
+          },
+        }
+
+        const flag: any = shallowRef(Dummy)
+
+        onMounted(() => {
+          flag.value = Parent
+        })
+        return () => {
+          return h(Suspense, null, {
+            default: () => h(RouterView, { name: flag.value }),
+          })
+        }
+      },
+    }
+
+    const root: any = nodeOps.createElement('div')
+
+    render(h(App), root)
+  })
+
   // #13453
   test('add new async deps during patching', async () => {
     const getComponent = (type: string) => {

+ 3 - 3
packages/runtime-core/src/renderer.ts

@@ -1463,9 +1463,9 @@ function baseCreateRenderer(
             // and continue the rest of operations once the deps are resolved
             nonHydratedAsyncRoot.asyncDep!.then(() => {
               // the instance may be destroyed during the time period
-              if (!instance.isUnmounted) {
-                componentUpdateFn()
-              }
+              queuePostRenderEffect(() => {
+                if (!instance.isUnmounted) update()
+              }, parentSuspense)
             })
             return
           }