Skip to content

Commit fc40ca0

Browse files
authored
fix(useTemplateRef): don't update setup ref for useTemplateRef key (#12756)
close #12749
1 parent 6798853 commit fc40ca0

File tree

3 files changed

+76
-16
lines changed

3 files changed

+76
-16
lines changed

packages/runtime-core/__tests__/helpers/useTemplateRef.spec.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
h,
44
nextTick,
55
nodeOps,
6+
onMounted,
67
ref,
78
render,
89
useTemplateRef,
@@ -254,6 +255,52 @@ describe('useTemplateRef', () => {
254255
}
255256
})
256257

258+
// #12749
259+
test(`don't update setup ref for useTemplateRef key`, () => {
260+
let foo: ShallowRef
261+
const Comp = {
262+
setup() {
263+
foo = useTemplateRef('bar')
264+
const bar = ref(null)
265+
onMounted(() => {
266+
expect(bar.value).toBe(null)
267+
})
268+
return { bar }
269+
},
270+
render() {
271+
return h('div', { ref: 'bar' })
272+
},
273+
}
274+
const root = nodeOps.createElement('div')
275+
render(h(Comp), root)
276+
expect(foo!.value).toBe(root.children[0])
277+
})
278+
279+
test(`don't update setup ref for useTemplateRef key (compiled in prod mode)`, () => {
280+
__DEV__ = false
281+
try {
282+
let foo: ReturnType<typeof ref>
283+
let fooRef: ShallowRef
284+
const Comp = {
285+
setup() {
286+
foo = ref('hello')
287+
fooRef = useTemplateRef('foo')
288+
return { foo }
289+
},
290+
render() {
291+
return h('input', { ref: foo, ref_key: 'foo' })
292+
},
293+
}
294+
const root = nodeOps.createElement('div')
295+
render(h(Comp), root)
296+
297+
expect(foo!.value).toBe('hello')
298+
expect(fooRef!.value).toBe(root.children[0])
299+
} finally {
300+
__DEV__ = true
301+
}
302+
})
303+
257304
test('should work when used as direct ref value with ref_key and ref_for (compiled in prod mode)', () => {
258305
__DEV__ = false
259306
try {

packages/runtime-core/src/helpers/useTemplateRef.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { type ShallowRef, readonly, shallowRef } from '@vue/reactivity'
2-
import { getCurrentInstance } from '../component'
2+
import { type Data, getCurrentInstance } from '../component'
33
import { warn } from '../warning'
44
import { EMPTY_OBJ } from '@vue/shared'
55

@@ -14,12 +14,7 @@ export function useTemplateRef<T = unknown, Keys extends string = string>(
1414
const r = shallowRef(null)
1515
if (i) {
1616
const refs = i.refs === EMPTY_OBJ ? (i.refs = {}) : i.refs
17-
let desc: PropertyDescriptor | undefined
18-
if (
19-
__DEV__ &&
20-
(desc = Object.getOwnPropertyDescriptor(refs, key)) &&
21-
!desc.configurable
22-
) {
17+
if (__DEV__ && isTemplateRefKey(refs, key)) {
2318
warn(`useTemplateRef('${key}') already exists.`)
2419
} else {
2520
Object.defineProperty(refs, key, {
@@ -40,3 +35,10 @@ export function useTemplateRef<T = unknown, Keys extends string = string>(
4035
}
4136
return ret
4237
}
38+
39+
export function isTemplateRefKey(refs: Data, key: string): boolean {
40+
let desc: PropertyDescriptor | undefined
41+
return !!(
42+
(desc = Object.getOwnPropertyDescriptor(refs, key)) && !desc.configurable
43+
)
44+
}

packages/runtime-core/src/rendererTemplateRef.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { ErrorCodes, callWithErrorHandling } from './errorHandling'
2222
import { type SchedulerJob, SchedulerJobFlags } from './scheduler'
2323
import { queuePostRenderEffect } from './renderer'
2424
import { type ComponentOptions, getComponentPublicInstance } from './component'
25-
import { knownTemplateRefs } from './helpers/useTemplateRef'
25+
import { isTemplateRefKey, knownTemplateRefs } from './helpers/useTemplateRef'
2626

2727
const pendingSetRefMap = new WeakMap<VNodeNormalizedRef, SchedulerJob>()
2828
/**
@@ -98,11 +98,23 @@ export function setRef(
9898
return false
9999
}
100100
}
101+
102+
// skip setting up ref if the key is from useTemplateRef
103+
if (isTemplateRefKey(refs, key)) {
104+
return false
105+
}
106+
101107
return hasOwn(rawSetupState, key)
102108
}
103109

104-
const canSetRef = (ref: VNodeRef) => {
105-
return !__DEV__ || !knownTemplateRefs.has(ref as any)
110+
const canSetRef = (ref: VNodeRef, key?: string) => {
111+
if (__DEV__ && knownTemplateRefs.has(ref as any)) {
112+
return false
113+
}
114+
if (key && isTemplateRefKey(refs, key)) {
115+
return false
116+
}
117+
return true
106118
}
107119

108120
// dynamic ref changed. unset old ref
@@ -114,12 +126,11 @@ export function setRef(
114126
setupState[oldRef] = null
115127
}
116128
} else if (isRef(oldRef)) {
117-
if (canSetRef(oldRef)) {
118-
oldRef.value = null
119-
}
120-
121129
// this type assertion is valid since `oldRef` has already been asserted to be non-null
122130
const oldRawRefAtom = oldRawRef as VNodeNormalizedRefAtom
131+
if (canSetRef(oldRef, oldRawRefAtom.k)) {
132+
oldRef.value = null
133+
}
123134
if (oldRawRefAtom.k) refs[oldRawRefAtom.k] = null
124135
}
125136
}
@@ -151,7 +162,7 @@ export function setRef(
151162
}
152163
} else {
153164
const newVal = [refValue]
154-
if (canSetRef(ref)) {
165+
if (canSetRef(ref, rawRef.k)) {
155166
ref.value = newVal
156167
}
157168
if (rawRef.k) refs[rawRef.k] = newVal
@@ -166,7 +177,7 @@ export function setRef(
166177
setupState[ref] = value
167178
}
168179
} else if (_isRef) {
169-
if (canSetRef(ref)) {
180+
if (canSetRef(ref, rawRef.k)) {
170181
ref.value = value
171182
}
172183
if (rawRef.k) refs[rawRef.k] = value

0 commit comments

Comments
 (0)