fix(runtime-core): avoid double-setting props when casting
fix #3371, close #3384
This commit is contained in:
parent
4ce0df6ef1
commit
0255be2f4b
@ -12,7 +12,8 @@ import {
|
||||
provide,
|
||||
inject,
|
||||
watch,
|
||||
toRefs
|
||||
toRefs,
|
||||
SetupContext
|
||||
} from '@vue/runtime-test'
|
||||
import { render as domRender, nextTick } from 'vue'
|
||||
|
||||
@ -508,4 +509,51 @@ describe('component props', () => {
|
||||
await nextTick()
|
||||
expect(changeSpy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
// #3371
|
||||
test(`avoid double-setting props when casting`, async () => {
|
||||
const Parent = {
|
||||
setup(props: any, { slots }: SetupContext) {
|
||||
const childProps = ref()
|
||||
const registerChildProps = (props: any) => {
|
||||
childProps.value = props
|
||||
}
|
||||
provide('register', registerChildProps)
|
||||
|
||||
return () => {
|
||||
// access the child component's props
|
||||
childProps.value && childProps.value.foo
|
||||
return slots.default!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Child = {
|
||||
props: {
|
||||
foo: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
setup(props: { foo: boolean }) {
|
||||
const register = inject('register') as any
|
||||
// 1. change the reactivity data of the parent component
|
||||
// 2. register its own props to the parent component
|
||||
register(props)
|
||||
|
||||
return () => 'foo'
|
||||
}
|
||||
}
|
||||
|
||||
const App = {
|
||||
setup() {
|
||||
return () => h(Parent, () => h(Child as any, { foo: '' }, () => null))
|
||||
}
|
||||
}
|
||||
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(App), root)
|
||||
await nextTick()
|
||||
expect(serializeInner(root)).toBe(`foo`)
|
||||
})
|
||||
})
|
||||
|
@ -312,6 +312,7 @@ function setFullProps(
|
||||
) {
|
||||
const [options, needCastKeys] = instance.propsOptions
|
||||
let hasAttrsChanged = false
|
||||
let rawCastValues: Data | undefined
|
||||
if (rawProps) {
|
||||
for (let key in rawProps) {
|
||||
// key, ref are reserved and never passed down
|
||||
@ -337,7 +338,11 @@ function setFullProps(
|
||||
// kebab -> camel conversion here we need to camelize the key.
|
||||
let camelKey
|
||||
if (options && hasOwn(options, (camelKey = camelize(key)))) {
|
||||
props[camelKey] = value
|
||||
if (!needCastKeys || !needCastKeys.includes(camelKey)) {
|
||||
props[camelKey] = value
|
||||
} else {
|
||||
;(rawCastValues || (rawCastValues = {}))[camelKey] = value
|
||||
}
|
||||
} else if (!isEmitListener(instance.emitsOptions, key)) {
|
||||
// Any non-declared (either as a prop or an emitted event) props are put
|
||||
// into a separate `attrs` object for spreading. Make sure to preserve
|
||||
@ -358,14 +363,13 @@ function setFullProps(
|
||||
}
|
||||
|
||||
if (needCastKeys) {
|
||||
const rawCurrentProps = toRaw(props)
|
||||
for (let i = 0; i < needCastKeys.length; i++) {
|
||||
const key = needCastKeys[i]
|
||||
props[key] = resolvePropValue(
|
||||
options!,
|
||||
rawCurrentProps,
|
||||
rawCastValues || EMPTY_OBJ,
|
||||
key,
|
||||
rawCurrentProps[key],
|
||||
rawCastValues && rawCastValues[key],
|
||||
instance
|
||||
)
|
||||
}
|
||||
@ -408,13 +412,13 @@ function resolvePropValue(
|
||||
}
|
||||
// boolean casting
|
||||
if (opt[BooleanFlags.shouldCast]) {
|
||||
if (!hasOwn(props, key) && !hasDefault) {
|
||||
value = false
|
||||
} else if (
|
||||
if (
|
||||
opt[BooleanFlags.shouldCastTrue] &&
|
||||
(value === '' || value === hyphenate(key))
|
||||
) {
|
||||
value = true
|
||||
} else if (!hasOwn(props, key) && !hasDefault) {
|
||||
value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user