fix(defineModel): support local mutation when only prop but no listener is passed

This commit is contained in:
Evan You 2023-12-30 08:57:55 +08:00
parent 6fab8551e4
commit 97ce041910
2 changed files with 48 additions and 1 deletions

View File

@ -279,6 +279,41 @@ describe('SFC <script setup> helpers', () => {
expect(serializeInner(root)).toBe('bar')
})
test('without parent listener (local mutation)', async () => {
let foo: any
const update = () => {
foo.value = 'bar'
}
const compRender = vi.fn()
const Comp = defineComponent({
props: ['foo'],
emits: ['update:foo'],
setup(props) {
foo = useModel(props, 'foo')
return () => {
compRender()
return foo.value
}
},
})
const root = nodeOps.createElement('div')
// provide initial value
render(h(Comp, { foo: 'initial' }), root)
expect(compRender).toBeCalledTimes(1)
expect(serializeInner(root)).toBe('initial')
expect(foo.value).toBe('initial')
update()
// when parent didn't provide value, local mutation is enabled
expect(foo.value).toBe('bar')
await nextTick()
expect(compRender).toBeCalledTimes(2)
expect(serializeInner(root)).toBe('bar')
})
test('default value', async () => {
let count: any
const inc = () => {

View File

@ -3,6 +3,7 @@ import {
type LooseRequired,
type Prettify,
type UnionToIntersection,
camelize,
extend,
hasChanged,
isArray,
@ -380,6 +381,8 @@ export function useModel(
return ref() as any
}
const camelizedName = camelize(name)
const res = customRef((track, trigger) => {
let localValue: any
watchSyncEffect(() => {
@ -396,7 +399,16 @@ export function useModel(
},
set(value) {
const rawProps = i.vnode!.props
if (!(rawProps && name in rawProps) && hasChanged(value, localValue)) {
if (
!(
rawProps &&
// check if parent has passed v-model
(name in rawProps || camelizedName in rawProps) &&
(`onUpdate:${name}` in rawProps ||
`onUpdate:${camelizedName}` in rawProps)
) &&
hasChanged(value, localValue)
) {
localValue = value
trigger()
}