Browse Source

fix(watch): cleanup watcher effect from scope when manually stopped (#9978)

Yang Mingshan 2 years ago
parent
commit
d2d89551bb

+ 31 - 0
packages/runtime-core/__tests__/apiWatch.spec.ts

@@ -1443,4 +1443,35 @@ describe('api: watch', () => {
     expect(spy1).toHaveBeenCalledTimes(1)
     expect(spy1).toHaveBeenCalledTimes(1)
     expect(spy2).toHaveBeenCalledTimes(1)
     expect(spy2).toHaveBeenCalledTimes(1)
   })
   })
+
+  test("effect should be removed from scope's effects after it is stopped", () => {
+    const num = ref(0)
+    let unwatch: () => void
+
+    let instance: ComponentInternalInstance
+    const Comp = {
+      setup() {
+        instance = getCurrentInstance()!
+        unwatch = watch(num, () => {
+          console.log(num.value)
+        })
+        return () => null
+      },
+    }
+    const root = nodeOps.createElement('div')
+    createApp(Comp).mount(root)
+    expect(instance!.scope.effects.length).toBe(2)
+    unwatch!()
+    expect(instance!.scope.effects.length).toBe(1)
+
+    const scope = effectScope()
+    scope.run(() => {
+      unwatch = watch(num, () => {
+        console.log(num.value)
+      })
+    })
+    expect(scope.effects.length).toBe(1)
+    unwatch!()
+    expect(scope.effects.length).toBe(0)
+  })
 })
 })

+ 4 - 2
packages/runtime-core/src/apiWatch.ts

@@ -5,6 +5,7 @@ import {
   ReactiveEffect,
   ReactiveEffect,
   ReactiveFlags,
   ReactiveFlags,
   type Ref,
   type Ref,
+  getCurrentScope,
   isReactive,
   isReactive,
   isRef,
   isRef,
   isShallow,
   isShallow,
@@ -394,10 +395,11 @@ function doWatch(
 
 
   const effect = new ReactiveEffect(getter, NOOP, scheduler)
   const effect = new ReactiveEffect(getter, NOOP, scheduler)
 
 
+  const scope = getCurrentScope()
   const unwatch = () => {
   const unwatch = () => {
     effect.stop()
     effect.stop()
-    if (instance && instance.scope) {
-      remove(instance.scope.effects!, effect)
+    if (scope) {
+      remove(scope.effects, effect)
     }
     }
   }
   }