]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core): fix async component ref handling (#3191)
authorHcySunYang <HcySunYang@outlook.com>
Mon, 1 Mar 2021 20:00:32 +0000 (04:00 +0800)
committerGitHub <noreply@github.com>
Mon, 1 Mar 2021 20:00:32 +0000 (15:00 -0500)
fix #3188

packages/runtime-core/__tests__/apiAsyncComponent.spec.ts
packages/runtime-core/src/renderer.ts

index 4f12d1c20be0e8d4419deea1b718c943bdc954be..6f16aa8d3037e6de8870c2211096da3afeb55838 100644 (file)
@@ -662,7 +662,7 @@ describe('api: defineAsyncComponent', () => {
         })
     )
 
-    const fooRef = ref()
+    const fooRef = ref<any>(null)
     const toggle = ref(true)
     const root = nodeOps.createElement('div')
     createApp({
@@ -697,4 +697,51 @@ describe('api: defineAsyncComponent', () => {
     expect(serializeInner(root)).toBe('resolved')
     expect(fooRef.value.id).toBe('foo')
   })
+
+  // #3188
+  test('the forwarded template ref should always exist when doing multi patching', async () => {
+    let resolve: (comp: Component) => void
+    const Foo = defineAsyncComponent(
+      () =>
+        new Promise(r => {
+          resolve = r as any
+        })
+    )
+
+    const fooRef = ref<any>(null)
+    const toggle = ref(true)
+    const updater = ref(0)
+
+    const root = nodeOps.createElement('div')
+    createApp({
+      render: () =>
+        toggle.value ? [h(Foo, { ref: fooRef }), updater.value] : null
+    }).mount(root)
+
+    expect(serializeInner(root)).toBe('<!---->0')
+    expect(fooRef.value).toBe(null)
+
+    resolve!({
+      data() {
+        return {
+          id: 'foo'
+        }
+      },
+      render: () => 'resolved'
+    })
+
+    await timeout()
+    expect(serializeInner(root)).toBe('resolved0')
+    expect(fooRef.value.id).toBe('foo')
+
+    updater.value++
+    await nextTick()
+    expect(serializeInner(root)).toBe('resolved1')
+    expect(fooRef.value.id).toBe('foo')
+
+    toggle.value = false
+    await nextTick()
+    expect(serializeInner(root)).toBe('<!---->')
+    expect(fooRef.value).toBe(null)
+  })
 })
index b7973284f705362a067b13e88ce5a3d874feedeb..2a0b6a312ce5db449816146cbcae555bded0a7f1 100644 (file)
@@ -313,9 +313,13 @@ export const setRef = (
   }
 
   let value: ComponentPublicInstance | RendererNode | Record<string, any> | null
-  if (!vnode || isAsyncWrapper(vnode)) {
+  if (!vnode) {
+    // means unmount
     value = null
   } else {
+    // when mounting async components, nothing needs to be done,
+    // because the template ref is forwarded to inner component
+    if (isAsyncWrapper(vnode)) return
     if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
       value = vnode.component!.exposed || vnode.component!.proxy
     } else {