Skip to content

Commit be69bee

Browse files
committed
fix(runtime-core): cloneVNode should preserve correct ctx instance when normalizing ref
fix #1311
1 parent 605953a commit be69bee

File tree

2 files changed

+48
-15
lines changed

2 files changed

+48
-15
lines changed

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

+44-12
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { Data } from '../src/component'
1414
import { ShapeFlags, PatchFlags } from '@vue/shared'
1515
import { h, reactive, isReactive } from '../src'
1616
import { createApp, nodeOps, serializeInner } from '@vue/runtime-test'
17+
import { setCurrentRenderingInstance } from '../src/componentRenderUtils'
1718

1819
describe('vnode', () => {
1920
test('create with just tag', () => {
@@ -198,24 +199,55 @@ describe('vnode', () => {
198199
expect(cloned2).toEqual(node2)
199200
expect(cloneVNode(node2)).toEqual(node2)
200201
expect(cloneVNode(node2)).toEqual(cloned2)
202+
})
201203

204+
test('cloneVNode key normalization', () => {
202205
// #1041 should use resolved key/ref
203206
expect(cloneVNode(createVNode('div', { key: 1 })).key).toBe(1)
204207
expect(cloneVNode(createVNode('div', { key: 1 }), { key: 2 }).key).toBe(2)
205208
expect(cloneVNode(createVNode('div'), { key: 2 }).key).toBe(2)
209+
})
210+
211+
// ref normalizes to [currentRenderingInstance, ref]
212+
test('cloneVNode ref normalization', () => {
213+
const mockInstance1 = {} as any
214+
const mockInstance2 = {} as any
215+
216+
setCurrentRenderingInstance(mockInstance1)
217+
const original = createVNode('div', { ref: 'foo' })
218+
expect(original.ref).toEqual([mockInstance1, 'foo'])
219+
220+
// clone and preserve original ref
221+
const cloned1 = cloneVNode(original)
222+
expect(cloned1.ref).toEqual([mockInstance1, 'foo'])
223+
224+
// cloning with new ref, but with same context instance
225+
const cloned2 = cloneVNode(original, { ref: 'bar' })
226+
expect(cloned2.ref).toEqual([mockInstance1, 'bar'])
227+
228+
// cloning and adding ref to original that has no ref
229+
const original2 = createVNode('div')
230+
const cloned3 = cloneVNode(original2, { ref: 'bar' })
231+
expect(cloned3.ref).toEqual([mockInstance1, 'bar'])
232+
233+
// cloning with different context instance
234+
setCurrentRenderingInstance(mockInstance2)
235+
236+
// clone and preserve original ref
237+
const cloned4 = cloneVNode(original)
238+
// #1311 should preserve original context instance!
239+
expect(cloned4.ref).toEqual([mockInstance1, 'foo'])
240+
241+
// cloning with new ref, but with same context instance
242+
const cloned5 = cloneVNode(original, { ref: 'bar' })
243+
// new ref should use current context instance and overwrite orgiinal
244+
expect(cloned5.ref).toEqual([mockInstance2, 'bar'])
245+
246+
// cloning and adding ref to original that has no ref
247+
const cloned6 = cloneVNode(original2, { ref: 'bar' })
248+
expect(cloned6.ref).toEqual([mockInstance2, 'bar'])
206249

207-
// ref normalizes to [currentRenderingInstance, ref]
208-
expect(cloneVNode(createVNode('div', { ref: 'foo' })).ref).toEqual([
209-
null,
210-
'foo'
211-
])
212-
expect(
213-
cloneVNode(createVNode('div', { ref: 'foo' }), { ref: 'bar' }).ref
214-
).toEqual([null, 'bar'])
215-
expect(cloneVNode(createVNode('div'), { ref: 'bar' }).ref).toEqual([
216-
null,
217-
'bar'
218-
])
250+
setCurrentRenderingInstance(null)
219251
})
220252

221253
describe('mergeProps', () => {

packages/runtime-core/src/vnode.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -277,12 +277,13 @@ export const InternalObjectKey = `__vInternal`
277277
const normalizeKey = ({ key }: VNodeProps): VNode['key'] =>
278278
key != null ? key : null
279279

280-
const normalizeRef = ({ ref }: VNodeProps): VNode['ref'] =>
281-
(ref != null
280+
const normalizeRef = ({ ref }: VNodeProps): VNode['ref'] => {
281+
return (ref != null
282282
? isArray(ref)
283283
? ref
284284
: [currentRenderingInstance!, ref]
285285
: null) as any
286+
}
286287

287288
export const createVNode = (__DEV__
288289
? createVNodeWithArgsTransform
@@ -420,7 +421,7 @@ export function cloneVNode<T, U>(
420421
type: vnode.type,
421422
props,
422423
key: props && normalizeKey(props),
423-
ref: props && normalizeRef(props),
424+
ref: extraProps && extraProps.ref ? normalizeRef(extraProps) : vnode.ref,
424425
scopeId: vnode.scopeId,
425426
children: vnode.children,
426427
target: vnode.target,

0 commit comments

Comments
 (0)