Skip to content

Commit 3991ff0

Browse files
authored
fix(runtime-core): separate null vs. non-null ref value updates (#1835)
fix #1789, fix #1834
1 parent b14f4a5 commit 3991ff0

File tree

2 files changed

+76
-5
lines changed

2 files changed

+76
-5
lines changed

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

+57
Original file line numberDiff line numberDiff line change
@@ -268,4 +268,61 @@ describe('api: template refs', () => {
268268
// ref should be updated
269269
expect(serializeInner(root)).toBe(`<div id="foo">foo</div>`)
270270
})
271+
272+
// #1834
273+
test('exchange refs', async () => {
274+
const refToggle = ref(false)
275+
const spy = jest.fn()
276+
277+
const Comp = {
278+
render(this: any) {
279+
return [
280+
h('p', { ref: refToggle.value ? 'foo' : 'bar' }),
281+
h('i', { ref: refToggle.value ? 'bar' : 'foo' })
282+
]
283+
},
284+
mounted(this: any) {
285+
spy(this.$refs.foo.tag, this.$refs.bar.tag)
286+
},
287+
updated(this: any) {
288+
spy(this.$refs.foo.tag, this.$refs.bar.tag)
289+
}
290+
}
291+
292+
const root = nodeOps.createElement('div')
293+
render(h(Comp), root)
294+
295+
expect(spy.mock.calls[0][0]).toBe('i')
296+
expect(spy.mock.calls[0][1]).toBe('p')
297+
refToggle.value = true
298+
await nextTick()
299+
expect(spy.mock.calls[1][0]).toBe('p')
300+
expect(spy.mock.calls[1][1]).toBe('i')
301+
})
302+
303+
// #1789
304+
test('toggle the same ref to different elements', async () => {
305+
const refToggle = ref(false)
306+
const spy = jest.fn()
307+
308+
const Comp = {
309+
render(this: any) {
310+
return refToggle.value ? h('p', { ref: 'foo' }) : h('i', { ref: 'foo' })
311+
},
312+
mounted(this: any) {
313+
spy(this.$refs.foo.tag)
314+
},
315+
updated(this: any) {
316+
spy(this.$refs.foo.tag)
317+
}
318+
}
319+
320+
const root = nodeOps.createElement('div')
321+
render(h(Comp), root)
322+
323+
expect(spy.mock.calls[0][0]).toBe('i')
324+
refToggle.value = true
325+
await nextTick()
326+
expect(spy.mock.calls[1][0]).toBe('p')
327+
})
271328
})

packages/runtime-core/src/renderer.ts

+19-5
Original file line numberDiff line numberDiff line change
@@ -319,14 +319,28 @@ export const setRef = (
319319
}
320320

321321
if (isString(ref)) {
322-
refs[ref] = value
323-
if (hasOwn(setupState, ref)) {
324-
queuePostRenderEffect(() => {
322+
const doSet = () => {
323+
refs[ref] = value
324+
if (hasOwn(setupState, ref)) {
325325
setupState[ref] = value
326-
}, parentSuspense)
326+
}
327+
}
328+
// #1789: for non-null values, set them after render
329+
// null values means this is unmount and it should not overwrite another
330+
// ref with the same key
331+
if (value) {
332+
queuePostRenderEffect(doSet, parentSuspense)
333+
} else {
334+
doSet()
327335
}
328336
} else if (isRef(ref)) {
329-
ref.value = value
337+
if (value) {
338+
queuePostRenderEffect(() => {
339+
ref.value = value
340+
}, parentSuspense)
341+
} else {
342+
ref.value = value
343+
}
330344
} else if (isFunction(ref)) {
331345
callWithErrorHandling(ref, parentComponent, ErrorCodes.FUNCTION_REF, [
332346
value,

0 commit comments

Comments
 (0)