]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(defineModel): detect changes respect custom getter and setter (#11543)
authorLiuSeen <91084928+liuseen-l@users.noreply.github.com>
Wed, 7 Aug 2024 14:10:01 +0000 (22:10 +0800)
committerGitHub <noreply@github.com>
Wed, 7 Aug 2024 14:10:01 +0000 (22:10 +0800)
fix: #11541
fix: #11526
close: #11527

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

index 4c30de2f26acfa8637b016f756276b416ba3b69a..3c724b0ba0010972bce7642a79744b29896dba13 100644 (file)
@@ -657,4 +657,96 @@ describe('useModel', () => {
     expect(setValue).toBeCalledTimes(2)
     expect(msg.value).toBe(defaultVal)
   })
+
+  // #11526
+  test('custom getter', () => {
+    let changeChildMsg!: (val: boolean) => void
+    const getter = (value: boolean) => !value
+
+    const Comp = defineComponent({
+      props: ['msg'],
+      emits: ['update:msg'],
+      setup(props) {
+        const childMsg = useModel(props, 'msg', {
+          get: getter,
+          set: value => !value,
+        })
+        changeChildMsg = (val: boolean) => (childMsg.value = val)
+        return () => {
+          return childMsg.value
+        }
+      },
+    })
+
+    const defaultVal = false
+    const msg = ref(defaultVal)
+    const Parent = defineComponent({
+      setup() {
+        return () =>
+          h(Comp, {
+            msg: msg.value,
+            'onUpdate:msg': val => {
+              msg.value = val
+            },
+          })
+      },
+    })
+
+    const root = nodeOps.createElement('div')
+    render(h(Parent), root)
+
+    changeChildMsg(!getter(msg.value))
+    expect(msg.value).toBe(true)
+
+    changeChildMsg(!getter(msg.value))
+    expect(msg.value).toBe(false)
+  })
+
+  // #11541
+  test('custom setter', () => {
+    let changeChildMsg!: (val: boolean) => void
+
+    const Comp = defineComponent({
+      props: ['msg'],
+      emits: ['update:msg'],
+      setup(props) {
+        const childMsg = useModel(props, 'msg', {
+          set: value => {
+            if (value === msg.value) {
+              return null
+            } else {
+              return value
+            }
+          },
+        })
+        changeChildMsg = (val: boolean) => (childMsg.value = val)
+        return () => {
+          return childMsg.value
+        }
+      },
+    })
+
+    const defaultVal = false
+    const msg = ref(defaultVal)
+    const Parent = defineComponent({
+      setup() {
+        return () =>
+          h(Comp, {
+            msg: msg.value,
+            'onUpdate:msg': val => {
+              msg.value = val
+            },
+          })
+      },
+    })
+
+    const root = nodeOps.createElement('div')
+    render(h(Parent), root)
+
+    changeChildMsg(true)
+    expect(msg.value).toBe(true)
+
+    changeChildMsg(true)
+    expect(msg.value).toBe(null)
+  })
 })
index 5bcd316931d6402331850e53a2d6fa9536b9e45e..8180fb32c472eac8d946e31094382eb71f2ba190 100644 (file)
@@ -51,8 +51,9 @@ export function useModel(
       },
 
       set(value) {
+        const emittedValue = options.set ? options.set(value) : value
         if (
-          !hasChanged(value, localValue) &&
+          !hasChanged(emittedValue, localValue) &&
           !(prevSetValue !== EMPTY_OBJ && hasChanged(value, prevSetValue))
         ) {
           return
@@ -74,7 +75,7 @@ export function useModel(
           localValue = value
           trigger()
         }
-        const emittedValue = options.set ? options.set(value) : value
+
         i.emit(`update:${name}`, emittedValue)
         // #10279: if the local value is converted via a setter but the value
         // emitted to parent was the same, the parent will not trigger any