From 8bcd29c764a4d77bc53bdc95694dedde14efa6ed Mon Sep 17 00:00:00 2001 From: HcySunYang Date: Fri, 2 Apr 2021 11:58:22 +0800 Subject: [PATCH] fix(reactivity): make it possible to get the computed's value in the unmounted hook --- packages/reactivity/__tests__/effect.spec.ts | 24 ++++++++++++++++++++ packages/reactivity/src/computed.ts | 3 ++- packages/reactivity/src/effect.ts | 10 +++++--- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/packages/reactivity/__tests__/effect.spec.ts b/packages/reactivity/__tests__/effect.spec.ts index 34a0976030a..3005ff4729a 100644 --- a/packages/reactivity/__tests__/effect.spec.ts +++ b/packages/reactivity/__tests__/effect.spec.ts @@ -730,6 +730,30 @@ describe('reactivity/effect', () => { expect(dummy).toBe(1) }) + // #3099 + it('create an effect with allowInactiveRun', () => { + let dummy + const obj = reactive({ prop: 1 }) + const queue: (() => void)[] = [] + const runner = effect( + () => { + dummy = obj.prop + }, + { + scheduler: e => queue.push(e), + allowInactiveRun: true + } + ) + obj.prop = 2 + expect(dummy).toBe(1) + expect(queue.length).toBe(1) + stop(runner) + + // an effect with allowInactiveRun should be executed even after it is stopped + queue.forEach(e => e()) + expect(dummy).toBe(2) + }) + it('events: onStop', () => { const onStop = jest.fn() const runner = effect(() => {}, { diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index c12f3e55d94..b72aa2d8ac3 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -41,7 +41,8 @@ class ComputedRefImpl { this._dirty = true trigger(toRaw(this), TriggerOpTypes.SET, 'value') } - } + }, + allowInactiveRun: true }) this[ReactiveFlags.IS_READONLY] = isReadonly diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 683f8fa9e3c..abd47d20b8e 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -1,5 +1,5 @@ import { TrackOpTypes, TriggerOpTypes } from './operations' -import { EMPTY_OBJ, isArray, isIntegerKey, isMap } from '@vue/shared' +import { isArray, isIntegerKey, isMap } from '@vue/shared' // The main WeakMap that stores {target -> key -> dep} connections. // Conceptually, it's easier to think of a dependency as a Dep class @@ -27,6 +27,7 @@ export interface ReactiveEffectOptions { onTrigger?: (event: DebuggerEvent) => void onStop?: () => void allowRecurse?: boolean + allowInactiveRun?: boolean } export type DebuggerEvent = { @@ -54,11 +55,14 @@ export function isEffect(fn: any): fn is ReactiveEffect { export function effect( fn: () => T, - options: ReactiveEffectOptions = EMPTY_OBJ + options: ReactiveEffectOptions = Object.create(null) ): ReactiveEffect { if (isEffect(fn)) { fn = fn.raw } + if (!('allowInactiveRun' in options)) { + options.allowInactiveRun = !options.scheduler + } const effect = createReactiveEffect(fn, options) if (!options.lazy) { effect() @@ -84,7 +88,7 @@ function createReactiveEffect( ): ReactiveEffect { const effect = function reactiveEffect(): unknown { if (!effect.active) { - return options.scheduler ? undefined : fn() + return options.allowInactiveRun ? fn() : undefined } if (!effectStack.includes(effect)) { cleanup(effect)