diff --git a/packages/reactivity/__tests__/effectScope.spec.ts b/packages/reactivity/__tests__/effectScope.spec.ts index b5bc970e224..55073470b81 100644 --- a/packages/reactivity/__tests__/effectScope.spec.ts +++ b/packages/reactivity/__tests__/effectScope.spec.ts @@ -191,6 +191,14 @@ describe('reactivity/effect/scope', () => { expect(dummy).toBe(7) }) + it('should derefence child scope from parent scope after stopping child scope (no memleaks)', async () => { + const parent = new EffectScope() + const child = parent.run(() => new EffectScope())! + expect(parent.effects.includes(child)).toBe(true) + child.stop() + expect(parent.effects.includes(child)).toBe(false) + }) + it('test with higher level APIs', async () => { const r = ref(1) diff --git a/packages/reactivity/src/effectScope.ts b/packages/reactivity/src/effectScope.ts index fdacffc541d..0526f90d9d3 100644 --- a/packages/reactivity/src/effectScope.ts +++ b/packages/reactivity/src/effectScope.ts @@ -1,3 +1,4 @@ +import { remove } from '@vue/shared' import { ReactiveEffect } from './effect' import { warn } from './warning' @@ -8,10 +9,12 @@ export class EffectScope { active = true effects: (ReactiveEffect | EffectScope)[] = [] cleanups: (() => void)[] = [] + parent: EffectScope | undefined constructor(detached = false) { if (!detached) { recordEffectScope(this) + this.parent = activeEffectScope } } @@ -42,11 +45,14 @@ export class EffectScope { } } - stop() { + stop(fromParent = false) { if (this.active) { - this.effects.forEach(e => e.stop()) + this.effects.forEach(e => e.stop(true)) this.cleanups.forEach(cleanup => cleanup()) this.active = false + if (!fromParent && this.parent) { + remove(this.parent.effects, this) + } } } }