fix(reactivity): ensure computed always expose value

fix #3099

Also changes the original fix for #910 by moving the fix from
reactivity to the scheduler
This commit is contained in:
Evan You
2021-05-27 20:53:21 -04:00
parent 32e21333dd
commit 03a7a73148
5 changed files with 52 additions and 42 deletions

View File

@@ -193,4 +193,10 @@ describe('reactivity/computed', () => {
expect(isReadonly(z)).toBe(false)
expect(isReadonly(z.value.a)).toBe(false)
})
it('should expose value when stopped', () => {
const x = computed(() => 1)
stop(x.effect)
expect(x.value).toBe(1)
})
})

View File

@@ -709,28 +709,6 @@ describe('reactivity/effect', () => {
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', () => {
const onStop = jest.fn()
const runner = effect(() => {}, {

View File

@@ -26,6 +26,21 @@ export interface ReactiveEffectOptions {
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
onStop?: () => void
/**
* Indicates whether the job is allowed to recursively trigger itself when
* managed by the scheduler.
*
* By default, a job cannot trigger itself because some built-in method calls,
* e.g. Array.prototype.push actually performs reads as well (#1740) which
* can lead to confusing infinite loops.
* The allowed cases are component update functions and watch callbacks.
* Component update functions may update child component props, which in turn
* trigger flush: "pre" watch callbacks that mutates state that the parent
* relies on (#1801). Watch callbacks doesn't track its dependencies so if it
* triggers itself again, it's likely intentional and it is the user's
* responsibility to perform recursive state mutation that eventually
* stabilizes (#1727).
*/
allowRecurse?: boolean
}
@@ -84,7 +99,7 @@ function createReactiveEffect<T = any>(
): ReactiveEffect<T> {
const effect = function reactiveEffect(): unknown {
if (!effect.active) {
return options.scheduler ? undefined : fn()
return fn()
}
if (!effectStack.includes(effect)) {
cleanup(effect)