From 93c95dd4cd416503f43a98a1455f62658d22b0b2 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 27 Sep 2024 23:10:11 +0800 Subject: [PATCH] fix(reactivity): fix nested batch edge case --- packages/reactivity/__tests__/watch.spec.ts | 31 +++++++++++++++++++++ packages/reactivity/src/effect.ts | 4 +-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/__tests__/watch.spec.ts b/packages/reactivity/__tests__/watch.spec.ts index f333d7c06e0..882e8a245fa 100644 --- a/packages/reactivity/__tests__/watch.spec.ts +++ b/packages/reactivity/__tests__/watch.spec.ts @@ -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, 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) + }) }) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index b32355081e7..dad800d146d 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -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 } }