fix(reactivity): scheduled effect should not execute if stopped

fix #910
This commit is contained in:
Evan You 2020-04-02 19:49:45 -04:00
parent 5c33776e67
commit 0764c33d3d
2 changed files with 40 additions and 22 deletions

View File

@ -678,6 +678,28 @@ describe('reactivity/effect', () => {
expect(dummy).toBe(3) expect(dummy).toBe(3)
}) })
it('stop with scheduler', () => {
let dummy
const obj = reactive({ prop: 1 })
const queue: (() => void)[] = []
const runner = effect(
() => {
dummy = obj.prop
},
{
scheduler: e => queue.push(e)
}
)
obj.prop = 2
expect(dummy).toBe(1)
expect(queue.length).toBe(1)
stop(runner)
// a scheduled effect should not execute anymore after stopped
queue.forEach(e => e())
expect(dummy).toBe(1)
})
it('events: onStop', () => { it('events: onStop', () => {
const onStop = jest.fn() const onStop = jest.fn()
const runner = effect(() => {}, { const runner = effect(() => {}, {

View File

@ -10,7 +10,7 @@ type KeyToDepMap = Map<any, Dep>
const targetMap = new WeakMap<any, KeyToDepMap>() const targetMap = new WeakMap<any, KeyToDepMap>()
export interface ReactiveEffect<T = any> { export interface ReactiveEffect<T = any> {
(): T (...args: any[]): T
_isEffect: true _isEffect: true
active: boolean active: boolean
raw: () => T raw: () => T
@ -75,11 +75,26 @@ export function stop(effect: ReactiveEffect) {
} }
function createReactiveEffect<T = any>( function createReactiveEffect<T = any>(
fn: () => T, fn: (...args: any[]) => T,
options: ReactiveEffectOptions options: ReactiveEffectOptions
): ReactiveEffect<T> { ): ReactiveEffect<T> {
const effect = function reactiveEffect(...args: unknown[]): unknown { const effect = function reactiveEffect(...args: unknown[]): unknown {
return run(effect, fn, args) if (!effect.active) {
return options.scheduler ? undefined : fn(...args)
}
if (!effectStack.includes(effect)) {
cleanup(effect)
try {
enableTracking()
effectStack.push(effect)
activeEffect = effect
return fn(...args)
} finally {
effectStack.pop()
resetTracking()
activeEffect = effectStack[effectStack.length - 1]
}
}
} as ReactiveEffect } as ReactiveEffect
effect._isEffect = true effect._isEffect = true
effect.active = true effect.active = true
@ -89,25 +104,6 @@ function createReactiveEffect<T = any>(
return effect return effect
} }
function run(effect: ReactiveEffect, fn: Function, args: unknown[]): unknown {
if (!effect.active) {
return fn(...args)
}
if (!effectStack.includes(effect)) {
cleanup(effect)
try {
enableTracking()
effectStack.push(effect)
activeEffect = effect
return fn(...args)
} finally {
effectStack.pop()
resetTracking()
activeEffect = effectStack[effectStack.length - 1]
}
}
}
function cleanup(effect: ReactiveEffect) { function cleanup(effect: ReactiveEffect) {
const { deps } = effect const { deps } = effect
if (deps.length) { if (deps.length) {