Skip to content

Commit e042888

Browse files
authored
fix(defineModel): detect changes respect custom getter and setter (vuejs#11543)
fix: vuejs#11541 fix: vuejs#11526 close: vuejs#11527
1 parent b1abac0 commit e042888

File tree

2 files changed

+95
-2
lines changed

2 files changed

+95
-2
lines changed

packages/runtime-core/__tests__/helpers/useModel.spec.ts

+92
Original file line numberDiff line numberDiff line change
@@ -657,4 +657,96 @@ describe('useModel', () => {
657657
expect(setValue).toBeCalledTimes(2)
658658
expect(msg.value).toBe(defaultVal)
659659
})
660+
661+
// #11526
662+
test('custom getter', () => {
663+
let changeChildMsg!: (val: boolean) => void
664+
const getter = (value: boolean) => !value
665+
666+
const Comp = defineComponent({
667+
props: ['msg'],
668+
emits: ['update:msg'],
669+
setup(props) {
670+
const childMsg = useModel(props, 'msg', {
671+
get: getter,
672+
set: value => !value,
673+
})
674+
changeChildMsg = (val: boolean) => (childMsg.value = val)
675+
return () => {
676+
return childMsg.value
677+
}
678+
},
679+
})
680+
681+
const defaultVal = false
682+
const msg = ref(defaultVal)
683+
const Parent = defineComponent({
684+
setup() {
685+
return () =>
686+
h(Comp, {
687+
msg: msg.value,
688+
'onUpdate:msg': val => {
689+
msg.value = val
690+
},
691+
})
692+
},
693+
})
694+
695+
const root = nodeOps.createElement('div')
696+
render(h(Parent), root)
697+
698+
changeChildMsg(!getter(msg.value))
699+
expect(msg.value).toBe(true)
700+
701+
changeChildMsg(!getter(msg.value))
702+
expect(msg.value).toBe(false)
703+
})
704+
705+
// #11541
706+
test('custom setter', () => {
707+
let changeChildMsg!: (val: boolean) => void
708+
709+
const Comp = defineComponent({
710+
props: ['msg'],
711+
emits: ['update:msg'],
712+
setup(props) {
713+
const childMsg = useModel(props, 'msg', {
714+
set: value => {
715+
if (value === msg.value) {
716+
return null
717+
} else {
718+
return value
719+
}
720+
},
721+
})
722+
changeChildMsg = (val: boolean) => (childMsg.value = val)
723+
return () => {
724+
return childMsg.value
725+
}
726+
},
727+
})
728+
729+
const defaultVal = false
730+
const msg = ref(defaultVal)
731+
const Parent = defineComponent({
732+
setup() {
733+
return () =>
734+
h(Comp, {
735+
msg: msg.value,
736+
'onUpdate:msg': val => {
737+
msg.value = val
738+
},
739+
})
740+
},
741+
})
742+
743+
const root = nodeOps.createElement('div')
744+
render(h(Parent), root)
745+
746+
changeChildMsg(true)
747+
expect(msg.value).toBe(true)
748+
749+
changeChildMsg(true)
750+
expect(msg.value).toBe(null)
751+
})
660752
})

packages/runtime-core/src/helpers/useModel.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,9 @@ export function useModel(
5151
},
5252

5353
set(value) {
54+
const emittedValue = options.set ? options.set(value) : value
5455
if (
55-
!hasChanged(value, localValue) &&
56+
!hasChanged(emittedValue, localValue) &&
5657
!(prevSetValue !== EMPTY_OBJ && hasChanged(value, prevSetValue))
5758
) {
5859
return
@@ -74,7 +75,7 @@ export function useModel(
7475
localValue = value
7576
trigger()
7677
}
77-
const emittedValue = options.set ? options.set(value) : value
78+
7879
i.emit(`update:${name}`, emittedValue)
7980
// #10279: if the local value is converted via a setter but the value
8081
// emitted to parent was the same, the parent will not trigger any

0 commit comments

Comments
 (0)