diff --git a/src/apis/effectScope.ts b/src/apis/effectScope.ts index 486a3871..1f661678 100644 --- a/src/apis/effectScope.ts +++ b/src/apis/effectScope.ts @@ -75,7 +75,11 @@ export function recordEffectScope( scope = scope || activeEffectScope if (scope && scope.active) { scope.effects.push(effect) + return } + // destory on parent component unmounted + const vm = getCurrentInstance()?.proxy + vm && vm.$on('hook:destroyed', () => effect.stop()) } export function effectScope(detached?: boolean) { diff --git a/test/v3/reactivity/effectScope.spec.ts b/test/v3/reactivity/effectScope.spec.ts index 79cc8c8c..c46497a1 100644 --- a/test/v3/reactivity/effectScope.spec.ts +++ b/test/v3/reactivity/effectScope.spec.ts @@ -8,6 +8,7 @@ import { computed, ref, ComputedRef, + createApp, } from '../../../src' import { mockWarn } from '../../helpers' @@ -251,4 +252,34 @@ describe('reactivity/effect/scope', () => { expect(watchSpy).toHaveBeenCalledTimes(1) expect(watchEffectSpy).toHaveBeenCalledTimes(2) }) + + it('should stop along with parent component', async () => { + let dummy, doubled + const counter = reactive({ num: 0 }) + + const root = document.createElement('div') + const vm = createApp({ + setup() { + const scope = new EffectScope() + scope.run(() => { + watchEffect(() => (dummy = counter.num)) + watchEffect(() => (doubled = counter.num * 2)) + }) + }, + }) + vm.mount(root) + + expect(dummy).toBe(0) + counter.num = 7 + await nextTick() + expect(dummy).toBe(7) + expect(doubled).toBe(14) + + vm.unmount() + + counter.num = 6 + await nextTick() + expect(dummy).toBe(7) + expect(doubled).toBe(14) + }) })