test('default value', () => {
let proxy: any
+ const defaultFn = jest.fn(() => ({ a: 1 }))
+
const Comp = {
props: {
foo: {
default: 1
},
bar: {
- default: () => ({ a: 1 })
+ default: defaultFn
}
},
render() {
const root = nodeOps.createElement('div')
render(h(Comp, { foo: 2 }), root)
expect(proxy.foo).toBe(2)
+ const prevBar = proxy.bar
+ 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)
expect(proxy.foo).toBe(1)
expect(proxy.bar).toEqual({ b: 2 })
+ expect(defaultFn).toHaveBeenCalledTimes(1)
render(h(Comp, { foo: 3, bar: { b: 3 } }), root)
expect(proxy.foo).toBe(3)
expect(proxy.bar).toEqual({ b: 3 })
+ expect(defaultFn).toHaveBeenCalledTimes(1)
render(h(Comp, { bar: { b: 4 } }), root)
expect(proxy.foo).toBe(1)
expect(proxy.bar).toEqual({ b: 4 })
+ expect(defaultFn).toHaveBeenCalledTimes(1)
})
test('optimized props updates', async () => {
export function updateProps(
instance: ComponentInternalInstance,
rawProps: Data | null,
+ rawPrevProps: Data | null,
optimized: boolean
) {
const {
((kebabKey = hyphenate(key)) === key || !hasOwn(rawProps, kebabKey)))
) {
if (options) {
- props[key] = resolvePropValue(
- options,
- rawProps || EMPTY_OBJ,
- key,
- undefined
- )
+ if (rawPrevProps && rawPrevProps[kebabKey!] !== undefined) {
+ props[key] = resolvePropValue(
+ options,
+ rawProps || EMPTY_OBJ,
+ key,
+ undefined
+ )
+ }
} else {
delete props[key]
}
}
}
- for (const key in attrs) {
- if (!rawProps || !hasOwn(rawProps, key)) {
- delete attrs[key]
+ // in the case of functional component w/o props declaration, props and
+ // attrs point to the same object so it should already have been updated.
+ if (attrs !== rawCurrentProps) {
+ for (const key in attrs) {
+ if (!rawProps || !hasOwn(rawProps, key)) {
+ delete attrs[key]
+ }
}
}
}
}
if (needCastKeys) {
+ const rawCurrentProps = toRaw(props)
for (let i = 0; i < needCastKeys.length; i++) {
const key = needCastKeys[i]
- props[key] = resolvePropValue(options!, props, key, props[key])
+ props[key] = resolvePropValue(
+ options!,
+ rawCurrentProps,
+ key,
+ rawCurrentProps[key]
+ )
}
}
}
optimized: boolean
) => {
nextVNode.component = instance
+ const prevProps = instance.vnode.props
instance.vnode = nextVNode
instance.next = null
- updateProps(instance, nextVNode.props, optimized)
+ updateProps(instance, nextVNode.props, prevProps, optimized)
updateSlots(instance, nextVNode.children)
}