]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(reactivity): fix nested batch edge case
authorEvan You <evan@vuejs.org>
Fri, 27 Sep 2024 15:10:11 +0000 (23:10 +0800)
committerEvan You <evan@vuejs.org>
Fri, 27 Sep 2024 15:10:20 +0000 (23:10 +0800)
packages/reactivity/__tests__/watch.spec.ts
packages/reactivity/src/effect.ts

index f333d7c06e033002606f5100138dc0c9620d6e24..882e8a245faca290b237ba6bc36203caf9db43bc 100644 (file)
@@ -229,4 +229,35 @@ describe('watch', () => {
     expect(r.value).toBe(1)
     expect(c.value).toBe(1)
   })
+
+  // edge case where a nested endBatch() causes an effect to be batched in a
+  // nested batch loop with its .next mutated, causing the outer loop to end
+  // early
+  test('nested batch edge case', () => {
+    // useClamp from VueUse
+    const clamp = (n: number, min: number, max: number) =>
+      Math.min(max, Math.max(min, n))
+    function useClamp(src: Ref<number>, min: number, max: number) {
+      return computed({
+        get() {
+          return (src.value = clamp(src.value, min, max))
+        },
+        set(val) {
+          src.value = clamp(val, min, max)
+        },
+      })
+    }
+
+    const src = ref(1)
+    const clamped = useClamp(src, 1, 5)
+    watch(src, val => (clamped.value = val))
+
+    const spy = vi.fn()
+    watch(clamped, spy)
+
+    src.value = 2
+    expect(spy).toHaveBeenCalledTimes(1)
+    src.value = 10
+    expect(spy).toHaveBeenCalledTimes(2)
+  })
 })
index b32355081e77a10c99b5e256268dd8739a9afc3a..dad800d146de1449a749f86d0c0a6da66fcb9182 100644 (file)
@@ -274,6 +274,8 @@ export function endBatch(): void {
     batchedSub = undefined
     // 2nd pass: run effects
     while (e) {
+      next = e.next
+      e.next = undefined
       e.flags &= ~EffectFlags.NOTIFIED
       if (e.flags & EffectFlags.ACTIVE) {
         try {
@@ -283,8 +285,6 @@ export function endBatch(): void {
           if (!error) error = err
         }
       }
-      next = e.next
-      e.next = undefined
       e = next
     }
   }