Skip to content

Commit 97ce041

Browse files
committed
fix(defineModel): support local mutation when only prop but no listener is passed
1 parent 6fab855 commit 97ce041

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

packages/runtime-core/__tests__/apiSetupHelpers.spec.ts

+35
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,41 @@ describe('SFC <script setup> helpers', () => {
279279
expect(serializeInner(root)).toBe('bar')
280280
})
281281

282+
test('without parent listener (local mutation)', async () => {
283+
let foo: any
284+
const update = () => {
285+
foo.value = 'bar'
286+
}
287+
288+
const compRender = vi.fn()
289+
const Comp = defineComponent({
290+
props: ['foo'],
291+
emits: ['update:foo'],
292+
setup(props) {
293+
foo = useModel(props, 'foo')
294+
return () => {
295+
compRender()
296+
return foo.value
297+
}
298+
},
299+
})
300+
301+
const root = nodeOps.createElement('div')
302+
// provide initial value
303+
render(h(Comp, { foo: 'initial' }), root)
304+
expect(compRender).toBeCalledTimes(1)
305+
expect(serializeInner(root)).toBe('initial')
306+
307+
expect(foo.value).toBe('initial')
308+
update()
309+
// when parent didn't provide value, local mutation is enabled
310+
expect(foo.value).toBe('bar')
311+
312+
await nextTick()
313+
expect(compRender).toBeCalledTimes(2)
314+
expect(serializeInner(root)).toBe('bar')
315+
})
316+
282317
test('default value', async () => {
283318
let count: any
284319
const inc = () => {

packages/runtime-core/src/apiSetupHelpers.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
type LooseRequired,
44
type Prettify,
55
type UnionToIntersection,
6+
camelize,
67
extend,
78
hasChanged,
89
isArray,
@@ -380,6 +381,8 @@ export function useModel(
380381
return ref() as any
381382
}
382383

384+
const camelizedName = camelize(name)
385+
383386
const res = customRef((track, trigger) => {
384387
let localValue: any
385388
watchSyncEffect(() => {
@@ -396,7 +399,16 @@ export function useModel(
396399
},
397400
set(value) {
398401
const rawProps = i.vnode!.props
399-
if (!(rawProps && name in rawProps) && hasChanged(value, localValue)) {
402+
if (
403+
!(
404+
rawProps &&
405+
// check if parent has passed v-model
406+
(name in rawProps || camelizedName in rawProps) &&
407+
(`onUpdate:${name}` in rawProps ||
408+
`onUpdate:${camelizedName}` in rawProps)
409+
) &&
410+
hasChanged(value, localValue)
411+
) {
400412
localValue = value
401413
trigger()
402414
}

0 commit comments

Comments
 (0)