Skip to content

Commit

Permalink
fix(useTemplateRef): properly fix readonly warning in dev and ensure …
Browse files Browse the repository at this point in the history
…prod behavior consistency

close #11808
close #11816
close #11810
  • Loading branch information
yyx990803 committed Sep 5, 2024
1 parent 46c3ab1 commit 9b7797d
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 9 deletions.
28 changes: 25 additions & 3 deletions packages/runtime-core/__tests__/helpers/useTemplateRef.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
type ShallowRef,
h,
nextTick,
nodeOps,
Expand Down Expand Up @@ -84,12 +85,12 @@ describe('useTemplateRef', () => {
})

// #11795
test('should work when variable name is same as key', () => {
let tRef
test('should not attempt to set when variable name is same as key', () => {
let tRef: ShallowRef
const key = 'refKey'
const Comp = {
setup() {
tRef = useTemplateRef(key)
tRef = useTemplateRef('_')
return {
[key]: tRef,
}
Expand All @@ -102,5 +103,26 @@ describe('useTemplateRef', () => {
render(h(Comp), root)

expect('target is readonly').not.toHaveBeenWarned()
expect(tRef!.value).toBe(null)
})

test('should work when used as direct ref value (compiled in prod mode)', () => {
__DEV__ = false
try {
let foo: ShallowRef
const Comp = {
setup() {
foo = useTemplateRef('foo')
return () => h('div', { ref: foo })
},
}
const root = nodeOps.createElement('div')
render(h(Comp), root)

expect('target is readonly').not.toHaveBeenWarned()
expect(foo!.value).toBe(root.children[0])
} finally {
__DEV__ = true
}
})
})
9 changes: 7 additions & 2 deletions packages/runtime-core/src/helpers/useTemplateRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import { getCurrentInstance } from '../component'
import { warn } from '../warning'
import { EMPTY_OBJ } from '@vue/shared'

export const knownTemplateRefs: WeakSet<ShallowRef> = new WeakSet()

export function useTemplateRef<T = unknown, Keys extends string = string>(
key: Keys,
): Readonly<ShallowRef<T | null>> {
const i = getCurrentInstance()
const r = shallowRef(null)
if (i) {
const refs = i.refs === EMPTY_OBJ ? (i.refs = {}) : i.refs

let desc: PropertyDescriptor | undefined
if (
__DEV__ &&
Expand All @@ -31,5 +32,9 @@ export function useTemplateRef<T = unknown, Keys extends string = string>(
`instance to be associated with.`,
)
}
return (__DEV__ ? readonly(r) : r) as any
const ret = __DEV__ ? readonly(r) : r
if (__DEV__) {
knownTemplateRefs.add(ret)
}
return ret
}
13 changes: 9 additions & 4 deletions packages/runtime-core/src/rendererTemplateRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import {
} from '@vue/shared'
import { isAsyncWrapper } from './apiAsyncComponent'
import { warn } from './warning'
import { isRef } from '@vue/reactivity'
import { isRef, toRaw } from '@vue/reactivity'
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
import type { SchedulerJob } from './scheduler'
import { queuePostRenderEffect } from './renderer'
import { getComponentPublicInstance } from './component'
import { knownTemplateRefs } from './helpers/useTemplateRef'

/**
* Function for handling a template ref
Expand Down Expand Up @@ -63,12 +64,16 @@ export function setRef(
const oldRef = oldRawRef && (oldRawRef as VNodeNormalizedRefAtom).r
const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
const setupState = owner.setupState
const rawSetupState = toRaw(setupState)
const canSetSetupRef =
setupState === EMPTY_OBJ
? () => false
: (key: string) =>
hasOwn(setupState, key) &&
!(Object.getOwnPropertyDescriptor(refs, key) || EMPTY_OBJ).get
: (key: string) => {
if (__DEV__ && knownTemplateRefs.has(rawSetupState[key] as any)) {
return false
}
return hasOwn(rawSetupState, key)
}

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

0 comments on commit 9b7797d

Please sign in to comment.