Browse Source

fix(reactivity): only clear notified flags for computed in first batch iteration

close #12045
Evan You 1 year ago
parent
commit
aa9ef2386a

+ 5 - 1
packages/reactivity/src/computed.ts

@@ -84,9 +84,13 @@ export class ComputedRefImpl<T = any> implements Subscriber {
    * @internal
    */
   isSSR: boolean
+  /**
+   * @internal
+   */
+  next?: Subscriber = undefined
+
   // for backwards compat
   effect: this = this
-
   // dev only
   onTrack?: (event: DebuggerEvent) => void
   // dev only

+ 8 - 1
packages/reactivity/src/effect.ts

@@ -261,13 +261,20 @@ export function endBatch(): void {
   while (batchedSub) {
     let e: Subscriber | undefined = batchedSub
     let next: Subscriber | undefined
+    // 1st pass: clear notified flags for computed upfront
+    // we use the ACTIVE flag as a discriminator between computed and effect,
+    // since NOTIFIED is useless for an inactive effect anyway.
     while (e) {
-      e.flags &= ~EffectFlags.NOTIFIED
+      if (!(e.flags & EffectFlags.ACTIVE)) {
+        e.flags &= ~EffectFlags.NOTIFIED
+      }
       e = e.next
     }
     e = batchedSub
     batchedSub = undefined
+    // 2nd pass: run effects
     while (e) {
+      e.flags &= ~EffectFlags.NOTIFIED
       if (e.flags & EffectFlags.ACTIVE) {
         try {
           // ACTIVE flag is effect-only

+ 27 - 1
packages/runtime-core/__tests__/apiWatch.spec.ts

@@ -1930,7 +1930,7 @@ describe('api: watch', () => {
     warn.mockRestore()
   })
 
-  it('should be executed correctly', () => {
+  test('should be executed correctly', () => {
     const v = ref(1)
     let foo = ''
 
@@ -1957,4 +1957,30 @@ describe('api: watch', () => {
     v.value++
     expect(foo).toBe('12')
   })
+
+  // 12045
+  test('sync watcher should not break pre watchers', async () => {
+    const count1 = ref(0)
+    const count2 = ref(0)
+
+    watch(
+      count1,
+      () => {
+        count2.value++
+      },
+      { flush: 'sync' },
+    )
+
+    const spy1 = vi.fn()
+    watch([count1, count2], spy1)
+
+    const spy2 = vi.fn()
+    watch(count1, spy2)
+
+    count1.value++
+
+    await nextTick()
+    expect(spy1).toHaveBeenCalled()
+    expect(spy2).toHaveBeenCalled()
+  })
 })