]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core): separate null vs. non-null ref value updates (#1835)
authorHcySunYang <HcySunYang@outlook.com>
Thu, 13 Aug 2020 16:27:17 +0000 (00:27 +0800)
committerGitHub <noreply@github.com>
Thu, 13 Aug 2020 16:27:17 +0000 (12:27 -0400)
fix #1789, fix #1834

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

index cb5da3a9533aa7181dc9ca25b6b9b58db4b7f2a7..808731c230e2d14c5e043678e310f67cd024ec1d 100644 (file)
@@ -268,4 +268,61 @@ describe('api: template refs', () => {
     // ref should be updated
     expect(serializeInner(root)).toBe(`<div id="foo">foo</div>`)
   })
+
+  // #1834
+  test('exchange refs', async () => {
+    const refToggle = ref(false)
+    const spy = jest.fn()
+
+    const Comp = {
+      render(this: any) {
+        return [
+          h('p', { ref: refToggle.value ? 'foo' : 'bar' }),
+          h('i', { ref: refToggle.value ? 'bar' : 'foo' })
+        ]
+      },
+      mounted(this: any) {
+        spy(this.$refs.foo.tag, this.$refs.bar.tag)
+      },
+      updated(this: any) {
+        spy(this.$refs.foo.tag, this.$refs.bar.tag)
+      }
+    }
+
+    const root = nodeOps.createElement('div')
+    render(h(Comp), root)
+
+    expect(spy.mock.calls[0][0]).toBe('i')
+    expect(spy.mock.calls[0][1]).toBe('p')
+    refToggle.value = true
+    await nextTick()
+    expect(spy.mock.calls[1][0]).toBe('p')
+    expect(spy.mock.calls[1][1]).toBe('i')
+  })
+
+  // #1789
+  test('toggle the same ref to different elements', async () => {
+    const refToggle = ref(false)
+    const spy = jest.fn()
+
+    const Comp = {
+      render(this: any) {
+        return refToggle.value ? h('p', { ref: 'foo' }) : h('i', { ref: 'foo' })
+      },
+      mounted(this: any) {
+        spy(this.$refs.foo.tag)
+      },
+      updated(this: any) {
+        spy(this.$refs.foo.tag)
+      }
+    }
+
+    const root = nodeOps.createElement('div')
+    render(h(Comp), root)
+
+    expect(spy.mock.calls[0][0]).toBe('i')
+    refToggle.value = true
+    await nextTick()
+    expect(spy.mock.calls[1][0]).toBe('p')
+  })
 })
index 9f045182ce72873b558a8ba6f52000e9b11587cb..6b3b2cfab1c8197200d8d016b3377b4701c691d9 100644 (file)
@@ -319,14 +319,28 @@ export const setRef = (
   }
 
   if (isString(ref)) {
-    refs[ref] = value
-    if (hasOwn(setupState, ref)) {
-      queuePostRenderEffect(() => {
+    const doSet = () => {
+      refs[ref] = value
+      if (hasOwn(setupState, ref)) {
         setupState[ref] = value
-      }, parentSuspense)
+      }
+    }
+    // #1789: for non-null values, set them after render
+    // null values means this is unmount and it should not overwrite another
+    // ref with the same key
+    if (value) {
+      queuePostRenderEffect(doSet, parentSuspense)
+    } else {
+      doSet()
     }
   } else if (isRef(ref)) {
-    ref.value = value
+    if (value) {
+      queuePostRenderEffect(() => {
+        ref.value = value
+      }, parentSuspense)
+    } else {
+      ref.value = value
+    }
   } else if (isFunction(ref)) {
     callWithErrorHandling(ref, parentComponent, ErrorCodes.FUNCTION_REF, [
       value,