* @internal
*/
export { createInternalObject } from './internalObject'
+/**
+ * @internal
+ */
+export { createCanSetSetupRefChecker } from './rendererTemplateRef'
import { isRef, toRaw } from '@vue/reactivity'
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
import { queuePostRenderEffect } from './renderer'
-import { type ComponentOptions, getComponentPublicInstance } from './component'
+import {
+ type ComponentOptions,
+ type Data,
+ getComponentPublicInstance,
+} from './component'
import { knownTemplateRefs } from './helpers/useTemplateRef'
/**
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) => {
- if (__DEV__) {
- if (hasOwn(rawSetupState, key) && !isRef(rawSetupState[key])) {
- warn(
- `Template ref "${key}" used on a non-ref value. ` +
- `It will not work in the production build.`,
- )
- }
-
- if (knownTemplateRefs.has(rawSetupState[key] as any)) {
- return false
- }
- }
- return hasOwn(rawSetupState, key)
- }
+ const canSetSetupRef = createCanSetSetupRefChecker(setupState)
// dynamic ref changed. unset old ref
if (oldRef != null && oldRef !== ref) {
}
}
}
+
+export function createCanSetSetupRefChecker(
+ setupState: Data,
+): (key: string) => boolean {
+ const rawSetupState = toRaw(setupState)
+ return setupState === EMPTY_OBJ
+ ? () => false
+ : (key: string) => {
+ if (__DEV__) {
+ if (hasOwn(rawSetupState, key) && !isRef(rawSetupState[key])) {
+ warn(
+ `Template ref "${key}" used on a non-ref value. ` +
+ `It will not work in the production build.`,
+ )
+ }
+
+ if (knownTemplateRefs.has(rawSetupState[key] as any)) {
+ return false
+ }
+ }
+ return hasOwn(rawSetupState, key)
+ }
+}
nextTick,
reactive,
ref,
+ shallowRef,
useTemplateRef,
watchEffect,
} from '@vue/runtime-dom'
const { render } = define({
setup() {
return {
- foo: fooEl,
- bar: barEl,
+ foo: shallowRef(fooEl),
+ bar: shallowRef(barEl),
}
},
render() {
})
const { host } = render()
expect(state.refKey).toBe(host.children[0])
+ expect('Template ref "refKey" used on a non-ref value').toHaveBeenWarned()
})
test('multiple root refs', () => {
expect(html()).toBe('<div>changed</div><!--dynamic-component-->')
})
+ test('should not attempt to set when variable name is same as key', () => {
+ let tRef: ShallowRef
+ const key = 'refKey'
+ define({
+ setup() {
+ tRef = useTemplateRef('_')
+ return {
+ [key]: tRef,
+ }
+ },
+ render() {
+ const n0 = template('<div></div>')() as Element
+ createTemplateRefSetter()(n0, key)
+ return n0
+ },
+ }).render()
+ 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 { host } = define({
+ setup() {
+ foo = useTemplateRef('foo')
+ const n0 = template('<div></div>')() as Element
+ createTemplateRefSetter()(n0, foo)
+ return n0
+ },
+ }).render()
+ expect('target is readonly').not.toHaveBeenWarned()
+ expect(foo!.value).toBe(host.children[0])
+ } finally {
+ __DEV__ = true
+ }
+ })
+
// TODO: can not reproduce in Vapor
// // #2078
// test('handling multiple merged refs', async () => {
ErrorCodes,
type SchedulerJob,
callWithErrorHandling,
+ createCanSetSetupRefChecker,
queuePostFlushCb,
warn,
} from '@vue/runtime-dom'
const refs =
instance.refs === EMPTY_OBJ ? (instance.refs = {}) : instance.refs
+ const canSetSetupRef = createCanSetSetupRefChecker(setupState)
// dynamic ref changed. unset old ref
if (oldRef != null && oldRef !== ref) {
if (isString(oldRef)) {
const doSet: SchedulerJob = () => {
if (refFor) {
existing = _isString
- ? __DEV__ && hasOwn(setupState, ref)
+ ? __DEV__ && canSetSetupRef(ref)
? setupState[ref]
: refs[ref]
: ref.value
existing = [refValue]
if (_isString) {
refs[ref] = existing
- if (__DEV__ && hasOwn(setupState, ref)) {
+ if (__DEV__ && canSetSetupRef(ref)) {
setupState[ref] = refs[ref]
// if setupState[ref] is a reactivity ref,
// the existing will also become reactivity too
}
} else if (_isString) {
refs[ref] = refValue
- if (__DEV__ && hasOwn(setupState, ref)) {
+ if (__DEV__ && canSetSetupRef(ref)) {
setupState[ref] = refValue
}
} else if (_isRef) {
remove(existing, refValue)
} else if (_isString) {
refs[ref] = null
- if (__DEV__ && hasOwn(setupState, ref)) {
+ if (__DEV__ && canSetSetupRef(ref)) {
setupState[ref] = null
}
} else if (_isRef) {