fix(runtime-core): should not cast prop value if prop did not change
fix #999
This commit is contained in:
parent
36d77f9a9e
commit
171cfa404f
@ -156,13 +156,15 @@ describe('component props', () => {
|
|||||||
|
|
||||||
test('default value', () => {
|
test('default value', () => {
|
||||||
let proxy: any
|
let proxy: any
|
||||||
|
const defaultFn = jest.fn(() => ({ a: 1 }))
|
||||||
|
|
||||||
const Comp = {
|
const Comp = {
|
||||||
props: {
|
props: {
|
||||||
foo: {
|
foo: {
|
||||||
default: 1
|
default: 1
|
||||||
},
|
},
|
||||||
bar: {
|
bar: {
|
||||||
default: () => ({ a: 1 })
|
default: defaultFn
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
@ -173,19 +175,32 @@ describe('component props', () => {
|
|||||||
const root = nodeOps.createElement('div')
|
const root = nodeOps.createElement('div')
|
||||||
render(h(Comp, { foo: 2 }), root)
|
render(h(Comp, { foo: 2 }), root)
|
||||||
expect(proxy.foo).toBe(2)
|
expect(proxy.foo).toBe(2)
|
||||||
|
const prevBar = proxy.bar
|
||||||
expect(proxy.bar).toEqual({ a: 1 })
|
expect(proxy.bar).toEqual({ a: 1 })
|
||||||
|
expect(defaultFn).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
// #999: updates should not cause default factory of unchanged prop to be
|
||||||
|
// called again
|
||||||
|
render(h(Comp, { foo: 3 }), root)
|
||||||
|
expect(proxy.foo).toBe(3)
|
||||||
|
expect(proxy.bar).toEqual({ a: 1 })
|
||||||
|
expect(proxy.bar).toBe(prevBar)
|
||||||
|
expect(defaultFn).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
render(h(Comp, { bar: { b: 2 } }), root)
|
render(h(Comp, { bar: { b: 2 } }), root)
|
||||||
expect(proxy.foo).toBe(1)
|
expect(proxy.foo).toBe(1)
|
||||||
expect(proxy.bar).toEqual({ b: 2 })
|
expect(proxy.bar).toEqual({ b: 2 })
|
||||||
|
expect(defaultFn).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
render(h(Comp, { foo: 3, bar: { b: 3 } }), root)
|
render(h(Comp, { foo: 3, bar: { b: 3 } }), root)
|
||||||
expect(proxy.foo).toBe(3)
|
expect(proxy.foo).toBe(3)
|
||||||
expect(proxy.bar).toEqual({ b: 3 })
|
expect(proxy.bar).toEqual({ b: 3 })
|
||||||
|
expect(defaultFn).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
render(h(Comp, { bar: { b: 4 } }), root)
|
render(h(Comp, { bar: { b: 4 } }), root)
|
||||||
expect(proxy.foo).toBe(1)
|
expect(proxy.foo).toBe(1)
|
||||||
expect(proxy.bar).toEqual({ b: 4 })
|
expect(proxy.bar).toEqual({ b: 4 })
|
||||||
|
expect(defaultFn).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('optimized props updates', async () => {
|
test('optimized props updates', async () => {
|
||||||
|
@ -130,6 +130,7 @@ export function initProps(
|
|||||||
export function updateProps(
|
export function updateProps(
|
||||||
instance: ComponentInternalInstance,
|
instance: ComponentInternalInstance,
|
||||||
rawProps: Data | null,
|
rawProps: Data | null,
|
||||||
|
rawPrevProps: Data | null,
|
||||||
optimized: boolean
|
optimized: boolean
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
@ -184,20 +185,26 @@ export function updateProps(
|
|||||||
((kebabKey = hyphenate(key)) === key || !hasOwn(rawProps, kebabKey)))
|
((kebabKey = hyphenate(key)) === key || !hasOwn(rawProps, kebabKey)))
|
||||||
) {
|
) {
|
||||||
if (options) {
|
if (options) {
|
||||||
props[key] = resolvePropValue(
|
if (rawPrevProps && rawPrevProps[kebabKey!] !== undefined) {
|
||||||
options,
|
props[key] = resolvePropValue(
|
||||||
rawProps || EMPTY_OBJ,
|
options,
|
||||||
key,
|
rawProps || EMPTY_OBJ,
|
||||||
undefined
|
key,
|
||||||
)
|
undefined
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
delete props[key]
|
delete props[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const key in attrs) {
|
// in the case of functional component w/o props declaration, props and
|
||||||
if (!rawProps || !hasOwn(rawProps, key)) {
|
// attrs point to the same object so it should already have been updated.
|
||||||
delete attrs[key]
|
if (attrs !== rawCurrentProps) {
|
||||||
|
for (const key in attrs) {
|
||||||
|
if (!rawProps || !hasOwn(rawProps, key)) {
|
||||||
|
delete attrs[key]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,9 +247,15 @@ function setFullProps(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (needCastKeys) {
|
if (needCastKeys) {
|
||||||
|
const rawCurrentProps = toRaw(props)
|
||||||
for (let i = 0; i < needCastKeys.length; i++) {
|
for (let i = 0; i < needCastKeys.length; i++) {
|
||||||
const key = needCastKeys[i]
|
const key = needCastKeys[i]
|
||||||
props[key] = resolvePropValue(options!, props, key, props[key])
|
props[key] = resolvePropValue(
|
||||||
|
options!,
|
||||||
|
rawCurrentProps,
|
||||||
|
key,
|
||||||
|
rawCurrentProps[key]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1246,9 +1246,10 @@ function baseCreateRenderer(
|
|||||||
optimized: boolean
|
optimized: boolean
|
||||||
) => {
|
) => {
|
||||||
nextVNode.component = instance
|
nextVNode.component = instance
|
||||||
|
const prevProps = instance.vnode.props
|
||||||
instance.vnode = nextVNode
|
instance.vnode = nextVNode
|
||||||
instance.next = null
|
instance.next = null
|
||||||
updateProps(instance, nextVNode.props, optimized)
|
updateProps(instance, nextVNode.props, prevProps, optimized)
|
||||||
updateSlots(instance, nextVNode.children)
|
updateSlots(instance, nextVNode.children)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user