@@ -64,7 +64,14 @@ export type VNodeRef =
64
64
| Ref
65
65
| ( ( ref : object | null , refs : Record < string , any > ) => void )
66
66
67
- export type VNodeNormalizedRef = [ ComponentInternalInstance , VNodeRef ]
67
+ export type VNodeNormalizedRefAtom = {
68
+ i : ComponentInternalInstance
69
+ r : VNodeRef
70
+ }
71
+
72
+ export type VNodeNormalizedRef =
73
+ | VNodeNormalizedRefAtom
74
+ | ( VNodeNormalizedRefAtom ) [ ]
68
75
69
76
type VNodeMountHook = ( vnode : VNode ) => void
70
77
type VNodeUpdateHook = ( vnode : VNode , oldVNode : VNode ) => void
@@ -289,11 +296,11 @@ export const InternalObjectKey = `__vInternal`
289
296
const normalizeKey = ( { key } : VNodeProps ) : VNode [ 'key' ] =>
290
297
key != null ? key : null
291
298
292
- const normalizeRef = ( { ref } : VNodeProps ) : VNode [ 'ref' ] => {
299
+ const normalizeRef = ( { ref } : VNodeProps ) : VNodeNormalizedRefAtom | null => {
293
300
return ( ref != null
294
301
? isArray ( ref )
295
302
? ref
296
- : [ currentRenderingInstance ! , ref ]
303
+ : { i : currentRenderingInstance , r : ref }
297
304
: null ) as any
298
305
}
299
306
@@ -317,7 +324,10 @@ function _createVNode(
317
324
}
318
325
319
326
if ( isVNode ( type ) ) {
320
- const cloned = cloneVNode ( type , props )
327
+ // createVNode receiving an existing vnode. This happens in cases like
328
+ // <component :is="vnode"/>
329
+ // #2078 make sure to merge refs during the clone instead of overwriting it
330
+ const cloned = cloneVNode ( type , props , true /* mergeRef: true */ )
321
331
if ( children ) {
322
332
normalizeChildren ( cloned , children )
323
333
}
@@ -429,19 +439,30 @@ function _createVNode(
429
439
430
440
export function cloneVNode < T , U > (
431
441
vnode : VNode < T , U > ,
432
- extraProps ?: Data & VNodeProps | null
442
+ extraProps ?: Data & VNodeProps | null ,
443
+ mergeRef = false
433
444
) : VNode < T , U > {
434
445
// This is intentionally NOT using spread or extend to avoid the runtime
435
446
// key enumeration cost.
436
- const { props, patchFlag } = vnode
447
+ const { props, ref , patchFlag } = vnode
437
448
const mergedProps = extraProps ? mergeProps ( props || { } , extraProps ) : props
438
449
return {
439
450
__v_isVNode : true ,
440
451
[ ReactiveFlags . SKIP ] : true ,
441
452
type : vnode . type ,
442
453
props : mergedProps ,
443
454
key : mergedProps && normalizeKey ( mergedProps ) ,
444
- ref : extraProps && extraProps . ref ? normalizeRef ( extraProps ) : vnode . ref ,
455
+ ref :
456
+ extraProps && extraProps . ref
457
+ ? // #2078 in the case of <component :is="vnode" ref="extra"/>
458
+ // if the vnode itself already has a ref, cloneVNode will need to merge
459
+ // the refs so the single vnode can be set on multiple refs
460
+ mergeRef && ref
461
+ ? isArray ( ref )
462
+ ? ref . concat ( normalizeRef ( extraProps ) ! )
463
+ : [ ref , normalizeRef ( extraProps ) ! ]
464
+ : normalizeRef ( extraProps )
465
+ : ref ,
445
466
scopeId : vnode . scopeId ,
446
467
children : vnode . children ,
447
468
target : vnode . target ,
0 commit comments