Browse Source

fix(reactiivty): avoid unnecessary watcher effect removal from inactive scope

close #5783
close #5806
Evan You 1 year ago
parent
commit
21932840ea

+ 37 - 0
packages/reactivity/__tests__/effectScope.spec.ts

@@ -322,4 +322,41 @@ describe('reactivity/effect/scope', () => {
     scope.resume()
     expect(fnSpy).toHaveBeenCalledTimes(3)
   })
+
+  test('removing a watcher while stopping its effectScope', async () => {
+    const count = ref(0)
+    const scope = effectScope()
+    let watcherCalls = 0
+    let cleanupCalls = 0
+
+    scope.run(() => {
+      const stop1 = watch(count, () => {
+        watcherCalls++
+      })
+      watch(count, (val, old, onCleanup) => {
+        watcherCalls++
+        onCleanup(() => {
+          cleanupCalls++
+          stop1()
+        })
+      })
+      watch(count, () => {
+        watcherCalls++
+      })
+    })
+
+    expect(watcherCalls).toBe(0)
+    expect(cleanupCalls).toBe(0)
+
+    count.value++
+    await nextTick()
+    expect(watcherCalls).toBe(3)
+    expect(cleanupCalls).toBe(0)
+
+    scope.stop()
+    count.value++
+    await nextTick()
+    expect(watcherCalls).toBe(3)
+    expect(cleanupCalls).toBe(1)
+  })
 })

+ 1 - 1
packages/reactivity/src/effectScope.ts

@@ -117,6 +117,7 @@ export class EffectScope {
 
   stop(fromParent?: boolean): void {
     if (this._active) {
+      this._active = false
       let i, l
       for (i = 0, l = this.effects.length; i < l; i++) {
         this.effects[i].stop()
@@ -139,7 +140,6 @@ export class EffectScope {
         }
       }
       this.parent = undefined
-      this._active = false
     }
   }
 }

+ 1 - 1
packages/reactivity/src/watch.ts

@@ -213,7 +213,7 @@ export function watch(
   const scope = getCurrentScope()
   const watchHandle: WatchHandle = () => {
     effect.stop()
-    if (scope) {
+    if (scope && scope.active) {
       remove(scope.effects, effect)
     }
   }