fix(runtime-core): separate null vs. non-null ref value updates (#1835)

fix #1789, fix #1834
This commit is contained in:
HcySunYang 2020-08-14 00:27:17 +08:00 committed by GitHub
parent b14f4a505b
commit 3991ff03ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 76 additions and 5 deletions

View File

@ -268,4 +268,61 @@ describe('api: template refs', () => {
// ref should be updated // ref should be updated
expect(serializeInner(root)).toBe(`<div id="foo">foo</div>`) 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')
})
}) })

View File

@ -319,14 +319,28 @@ export const setRef = (
} }
if (isString(ref)) { if (isString(ref)) {
refs[ref] = value const doSet = () => {
if (hasOwn(setupState, ref)) { refs[ref] = value
queuePostRenderEffect(() => { if (hasOwn(setupState, ref)) {
setupState[ref] = value 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)) { } else if (isRef(ref)) {
ref.value = value if (value) {
queuePostRenderEffect(() => {
ref.value = value
}, parentSuspense)
} else {
ref.value = value
}
} else if (isFunction(ref)) { } else if (isFunction(ref)) {
callWithErrorHandling(ref, parentComponent, ErrorCodes.FUNCTION_REF, [ callWithErrorHandling(ref, parentComponent, ErrorCodes.FUNCTION_REF, [
value, value,