Skip to content

Commit 9b7797d

Browse files
committed
fix(useTemplateRef): properly fix readonly warning in dev and ensure prod behavior consistency
close #11808 close #11816 close #11810
1 parent 46c3ab1 commit 9b7797d

File tree

3 files changed

+41
-9
lines changed

3 files changed

+41
-9
lines changed

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

+25-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
type ShallowRef,
23
h,
34
nextTick,
45
nodeOps,
@@ -84,12 +85,12 @@ describe('useTemplateRef', () => {
8485
})
8586

8687
// #11795
87-
test('should work when variable name is same as key', () => {
88-
let tRef
88+
test('should not attempt to set when variable name is same as key', () => {
89+
let tRef: ShallowRef
8990
const key = 'refKey'
9091
const Comp = {
9192
setup() {
92-
tRef = useTemplateRef(key)
93+
tRef = useTemplateRef('_')
9394
return {
9495
[key]: tRef,
9596
}
@@ -102,5 +103,26 @@ describe('useTemplateRef', () => {
102103
render(h(Comp), root)
103104

104105
expect('target is readonly').not.toHaveBeenWarned()
106+
expect(tRef!.value).toBe(null)
107+
})
108+
109+
test('should work when used as direct ref value (compiled in prod mode)', () => {
110+
__DEV__ = false
111+
try {
112+
let foo: ShallowRef
113+
const Comp = {
114+
setup() {
115+
foo = useTemplateRef('foo')
116+
return () => h('div', { ref: foo })
117+
},
118+
}
119+
const root = nodeOps.createElement('div')
120+
render(h(Comp), root)
121+
122+
expect('target is readonly').not.toHaveBeenWarned()
123+
expect(foo!.value).toBe(root.children[0])
124+
} finally {
125+
__DEV__ = true
126+
}
105127
})
106128
})

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

+7-2
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ import { getCurrentInstance } from '../component'
33
import { warn } from '../warning'
44
import { EMPTY_OBJ } from '@vue/shared'
55

6+
export const knownTemplateRefs: WeakSet<ShallowRef> = new WeakSet()
7+
68
export function useTemplateRef<T = unknown, Keys extends string = string>(
79
key: Keys,
810
): Readonly<ShallowRef<T | null>> {
911
const i = getCurrentInstance()
1012
const r = shallowRef(null)
1113
if (i) {
1214
const refs = i.refs === EMPTY_OBJ ? (i.refs = {}) : i.refs
13-
1415
let desc: PropertyDescriptor | undefined
1516
if (
1617
__DEV__ &&
@@ -31,5 +32,9 @@ export function useTemplateRef<T = unknown, Keys extends string = string>(
3132
`instance to be associated with.`,
3233
)
3334
}
34-
return (__DEV__ ? readonly(r) : r) as any
35+
const ret = __DEV__ ? readonly(r) : r
36+
if (__DEV__) {
37+
knownTemplateRefs.add(ret)
38+
}
39+
return ret
3540
}

packages/runtime-core/src/rendererTemplateRef.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ import {
1111
} from '@vue/shared'
1212
import { isAsyncWrapper } from './apiAsyncComponent'
1313
import { warn } from './warning'
14-
import { isRef } from '@vue/reactivity'
14+
import { isRef, toRaw } from '@vue/reactivity'
1515
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
1616
import type { SchedulerJob } from './scheduler'
1717
import { queuePostRenderEffect } from './renderer'
1818
import { getComponentPublicInstance } from './component'
19+
import { knownTemplateRefs } from './helpers/useTemplateRef'
1920

2021
/**
2122
* Function for handling a template ref
@@ -63,12 +64,16 @@ export function setRef(
6364
const oldRef = oldRawRef && (oldRawRef as VNodeNormalizedRefAtom).r
6465
const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
6566
const setupState = owner.setupState
67+
const rawSetupState = toRaw(setupState)
6668
const canSetSetupRef =
6769
setupState === EMPTY_OBJ
6870
? () => false
69-
: (key: string) =>
70-
hasOwn(setupState, key) &&
71-
!(Object.getOwnPropertyDescriptor(refs, key) || EMPTY_OBJ).get
71+
: (key: string) => {
72+
if (__DEV__ && knownTemplateRefs.has(rawSetupState[key] as any)) {
73+
return false
74+
}
75+
return hasOwn(rawSetupState, key)
76+
}
7277

7378
// dynamic ref changed. unset old ref
7479
if (oldRef != null && oldRef !== ref) {

0 commit comments

Comments
 (0)