]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(reactivity): fix ref tracking of self-stopping effects
authorEvan You <yyx990803@gmail.com>
Wed, 13 Apr 2022 09:00:31 +0000 (17:00 +0800)
committerEvan You <yyx990803@gmail.com>
Wed, 13 Apr 2022 09:00:31 +0000 (17:00 +0800)
close #5707

packages/reactivity/__tests__/effect.spec.ts
packages/reactivity/src/effect.ts

index 77d069fc6187e61ec1f8f359d499d8d49901139b..34b9c575c93306a0f3737fef00fbcd97ebf827b9 100644 (file)
@@ -1,4 +1,5 @@
 import {
+  ref,
   reactive,
   effect,
   stop,
@@ -801,6 +802,26 @@ describe('reactivity/effect', () => {
     expect(dummy).toBe(3)
   })
 
+  // #5707
+  // when an effect completes its run, it should clear the tracking bits of
+  // its tracked deps. However, if the effect stops itself, the deps list is
+  // emptied so their bits are never cleared.
+  it('edge case: self-stopping effect tracking ref', () => {
+    const c = ref(true)
+    const runner = effect(() => {
+      // reference ref
+      if (!c.value) {
+        // stop itself while running
+        stop(runner)
+      }
+    })
+    // trigger run
+    c.value = !c.value
+    // should clear bits
+    expect((c as any).dep.w).toBe(0)
+    expect((c as any).dep.n).toBe(0)
+  })
+
   it('events: onStop', () => {
     const onStop = jest.fn()
     const runner = effect(() => {}, {
index b69b238975ea8668f5563de9d529779bb6e97aa3..77312eaa2cf6cb496b9e4fd751e5c4d676ea5fe3 100644 (file)
@@ -64,6 +64,10 @@ export class ReactiveEffect<T = any> {
    * @internal
    */
   allowRecurse?: boolean
+  /**
+   * @internal
+   */
+  private deferStop?: boolean
 
   onStop?: () => void
   // dev only
@@ -114,11 +118,18 @@ export class ReactiveEffect<T = any> {
       activeEffect = this.parent
       shouldTrack = lastShouldTrack
       this.parent = undefined
+
+      if (this.deferStop) {
+        this.stop()
+      }
     }
   }
 
   stop() {
-    if (this.active) {
+    // stopped while running itself - defer the cleanup
+    if (activeEffect === this) {
+      this.deferStop = true
+    } else if (this.active) {
       cleanupEffect(this)
       if (this.onStop) {
         this.onStop()