From 800207c473c7d6dfcdc883100a3d443fc5ad2e39 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 14 Oct 2022 10:51:07 +0800 Subject: [PATCH] fix(effectScope): calling off() of a detached scope should not break currentScope fix #12825 --- src/v3/reactivity/effectScope.ts | 9 ++++----- test/unit/features/v3/reactivity/effectScope.spec.ts | 11 +++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/v3/reactivity/effectScope.ts b/src/v3/reactivity/effectScope.ts index 2ba50cd9ca8..f60e5fccf23 100644 --- a/src/v3/reactivity/effectScope.ts +++ b/src/v3/reactivity/effectScope.ts @@ -16,9 +16,7 @@ export class EffectScope { * @internal */ cleanups: (() => void)[] = [] - /** - * only assigned by undetached scope * @internal */ parent: EffectScope | undefined @@ -38,9 +36,9 @@ export class EffectScope { */ private index: number | undefined - constructor(detached = false) { + constructor(public detached = false) { + this.parent = activeEffectScope if (!detached && activeEffectScope) { - this.parent = activeEffectScope this.index = (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push( this @@ -93,7 +91,7 @@ export class EffectScope { } } // nested scope, dereference from parent to avoid memory leaks - if (this.parent && !fromParent) { + if (!this.detached && this.parent && !fromParent) { // optimized O(1) removal const last = this.parent.scopes!.pop() if (last && last !== this) { @@ -101,6 +99,7 @@ export class EffectScope { last.index = this.index! } } + this.parent = undefined this.active = false } } diff --git a/test/unit/features/v3/reactivity/effectScope.spec.ts b/test/unit/features/v3/reactivity/effectScope.spec.ts index b82127cdc76..6b837e67cdc 100644 --- a/test/unit/features/v3/reactivity/effectScope.spec.ts +++ b/test/unit/features/v3/reactivity/effectScope.spec.ts @@ -279,4 +279,15 @@ describe('reactivity/effectScope', () => { expect(getCurrentScope()).toBe(currentScope) }) }) + + it('calling .off() of a detached scope inside an active scope should not break currentScope', () => { + const parentScope = new EffectScope() + + parentScope.run(() => { + const childScope = new EffectScope(true) + childScope.on() + childScope.off() + expect(getCurrentScope()).toBe(parentScope) + }) + }) })