parentSuspense?: SuspenseBoundary | null,
unmountChildren?: UnmountChildrenFn
): void
- forcePatchProp?(el: HostElement, key: string): boolean
insert(el: HostNode, parent: HostElement, anchor?: HostNode | null): void
remove(el: HostNode): void
createElement(
? queueEffectWithSuspense
: queuePostFlushCb
-export const setRef = (
- rawRef: VNodeNormalizedRef,
- oldRawRef: VNodeNormalizedRef | null,
- parentSuspense: SuspenseBoundary | null,
- vnode: VNode,
- isUnmount = false
-) => {
- if (isArray(rawRef)) {
- rawRef.forEach((r, i) =>
- setRef(
- r,
- oldRawRef && (isArray(oldRawRef) ? oldRawRef[i] : oldRawRef),
- parentSuspense,
- vnode,
- isUnmount
- )
- )
- return
- }
-
- if (isAsyncWrapper(vnode) && !isUnmount) {
- // when mounting async components, nothing needs to be done,
- // because the template ref is forwarded to inner component
- return
- }
-
- const refValue =
- vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT
- ? getExposeProxy(vnode.component!) || vnode.component!.proxy
- : vnode.el
- const value = isUnmount ? null : refValue
-
- const { i: owner, r: ref } = rawRef
- if (__DEV__ && !owner) {
- warn(
- `Missing ref owner context. ref cannot be used on hoisted vnodes. ` +
- `A vnode with ref must be created inside the render function.`
- )
- return
- }
- const oldRef = oldRawRef && (oldRawRef as VNodeNormalizedRefAtom).r
- const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
- const setupState = owner.setupState
-
- // dynamic ref changed. unset old ref
- if (oldRef != null && oldRef !== ref) {
- if (isString(oldRef)) {
- refs[oldRef] = null
- if (hasOwn(setupState, oldRef)) {
- setupState[oldRef] = null
- }
- } else if (isRef(oldRef)) {
- oldRef.value = null
- }
- }
-
- if (isString(ref)) {
- const doSet = () => {
- if (__COMPAT__ && isCompatEnabled(DeprecationTypes.V_FOR_REF, owner)) {
- registerLegacyRef(refs, ref, refValue, owner, rawRef.f, isUnmount)
- } else {
- refs[ref] = value
- }
- if (hasOwn(setupState, ref)) {
- setupState[ref] = value
- }
- }
- // #1789: for non-null values, set them after render
- // null values means this is unmount and it should not overwrite another
- // ref with the same key
- if (value) {
- ;(doSet as SchedulerJob).id = -1
- queuePostRenderEffect(doSet, parentSuspense)
- } else {
- doSet()
- }
- } else if (isRef(ref)) {
- const doSet = () => {
- ref.value = value
- }
- if (value) {
- ;(doSet as SchedulerJob).id = -1
- queuePostRenderEffect(doSet, parentSuspense)
- } else {
- doSet()
- }
- } else if (isFunction(ref)) {
- callWithErrorHandling(ref, owner, ErrorCodes.FUNCTION_REF, [value, refs])
- } else if (__DEV__) {
- warn('Invalid template ref type:', value, `(${typeof value})`)
- }
-}
-
/**
* The createRenderer function accepts two generic arguments:
* HostNode and HostElement, corresponding to Node and Element types in the
insert: hostInsert,
remove: hostRemove,
patchProp: hostPatchProp,
- forcePatchProp: hostForcePatchProp,
createElement: hostCreateElement,
createText: hostCreateText,
createComment: hostCreateComment,
// props
if (props) {
for (const key in props) {
- if (!isReservedProp(key)) {
+ if (key !== 'value' && !isReservedProp(key)) {
hostPatchProp(
el,
key,
)
}
}
+ /**
+ * Special case for setting value on DOM elements:
+ * - it can be order-sensitive (e.g. should be set *after* min/max, #2325, #4024)
+ * - it needs to be forced (#1471)
+ * #2353 proposes adding another renderer option to configure this, but
+ * the properties affects are so finite it is worth special casing it
+ * here to reduce the complexity. (Special casing it also should not
+ * affect non-DOM renderers)
+ */
+ if ('value' in props) {
+ hostPatchProp(el, 'value', null, props.value)
+ }
if ((vnodeHook = props.onVnodeBeforeMount)) {
invokeVNodeHook(vnodeHook, parentComponent, vnode)
}
const key = propsToUpdate[i]
const prev = oldProps[key]
const next = newProps[key]
- if (
- next !== prev ||
- (hostForcePatchProp && hostForcePatchProp(el, key))
- ) {
+ // #1471 force patch value
+ if (next !== prev || key === 'value') {
hostPatchProp(
el,
key,
if (isReservedProp(key)) continue
const next = newProps[key]
const prev = oldProps[key]
- if (
- next !== prev ||
- (hostForcePatchProp && hostForcePatchProp(el, key))
- ) {
+ // defer patching value
+ if (next !== prev && key !== 'value') {
hostPatchProp(
el,
key,
}
}
}
+ if ('value' in newProps) {
+ hostPatchProp(el, 'value', oldProps.value, newProps.value)
+ }
}
}
}
}
+export function setRef(
+ rawRef: VNodeNormalizedRef,
+ oldRawRef: VNodeNormalizedRef | null,
+ parentSuspense: SuspenseBoundary | null,
+ vnode: VNode,
+ isUnmount = false
+) {
+ if (isArray(rawRef)) {
+ rawRef.forEach((r, i) =>
+ setRef(
+ r,
+ oldRawRef && (isArray(oldRawRef) ? oldRawRef[i] : oldRawRef),
+ parentSuspense,
+ vnode,
+ isUnmount
+ )
+ )
+ return
+ }
+
+ if (isAsyncWrapper(vnode) && !isUnmount) {
+ // when mounting async components, nothing needs to be done,
+ // because the template ref is forwarded to inner component
+ return
+ }
+
+ const refValue =
+ vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT
+ ? getExposeProxy(vnode.component!) || vnode.component!.proxy
+ : vnode.el
+ const value = isUnmount ? null : refValue
+
+ const { i: owner, r: ref } = rawRef
+ if (__DEV__ && !owner) {
+ warn(
+ `Missing ref owner context. ref cannot be used on hoisted vnodes. ` +
+ `A vnode with ref must be created inside the render function.`
+ )
+ return
+ }
+ const oldRef = oldRawRef && (oldRawRef as VNodeNormalizedRefAtom).r
+ const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
+ const setupState = owner.setupState
+
+ // dynamic ref changed. unset old ref
+ if (oldRef != null && oldRef !== ref) {
+ if (isString(oldRef)) {
+ refs[oldRef] = null
+ if (hasOwn(setupState, oldRef)) {
+ setupState[oldRef] = null
+ }
+ } else if (isRef(oldRef)) {
+ oldRef.value = null
+ }
+ }
+
+ if (isString(ref)) {
+ const doSet = () => {
+ if (__COMPAT__ && isCompatEnabled(DeprecationTypes.V_FOR_REF, owner)) {
+ registerLegacyRef(refs, ref, refValue, owner, rawRef.f, isUnmount)
+ } else {
+ refs[ref] = value
+ }
+ if (hasOwn(setupState, ref)) {
+ setupState[ref] = value
+ }
+ }
+ // #1789: for non-null values, set them after render
+ // null values means this is unmount and it should not overwrite another
+ // ref with the same key
+ if (value) {
+ ;(doSet as SchedulerJob).id = -1
+ queuePostRenderEffect(doSet, parentSuspense)
+ } else {
+ doSet()
+ }
+ } else if (isRef(ref)) {
+ const doSet = () => {
+ ref.value = value
+ }
+ if (value) {
+ ;(doSet as SchedulerJob).id = -1
+ queuePostRenderEffect(doSet, parentSuspense)
+ } else {
+ doSet()
+ }
+ } else if (isFunction(ref)) {
+ callWithErrorHandling(ref, owner, ErrorCodes.FUNCTION_REF, [value, refs])
+ } else if (__DEV__) {
+ warn('Invalid template ref type:', value, `(${typeof value})`)
+ }
+}
+
export function invokeVNodeHook(
hook: VNodeHook,
instance: ComponentInternalInstance | null,