fix(reactivity): fix ref tracking of self-stopping effects

close #5707
This commit is contained in:
Evan You 2022-04-13 17:00:31 +08:00
parent f4f5e80a19
commit 154233abdb
2 changed files with 33 additions and 1 deletions

View File

@ -1,4 +1,5 @@
import { import {
ref,
reactive, reactive,
effect, effect,
stop, stop,
@ -801,6 +802,26 @@ describe('reactivity/effect', () => {
expect(dummy).toBe(3) expect(dummy).toBe(3)
}) })
// #5707
// when an effect completes its run, it should clear the tracking bits of
// its tracked deps. However, if the effect stops itself, the deps list is
// emptied so their bits are never cleared.
it('edge case: self-stopping effect tracking ref', () => {
const c = ref(true)
const runner = effect(() => {
// reference ref
if (!c.value) {
// stop itself while running
stop(runner)
}
})
// trigger run
c.value = !c.value
// should clear bits
expect((c as any).dep.w).toBe(0)
expect((c as any).dep.n).toBe(0)
})
it('events: onStop', () => { it('events: onStop', () => {
const onStop = jest.fn() const onStop = jest.fn()
const runner = effect(() => {}, { const runner = effect(() => {}, {

View File

@ -64,6 +64,10 @@ export class ReactiveEffect<T = any> {
* @internal * @internal
*/ */
allowRecurse?: boolean allowRecurse?: boolean
/**
* @internal
*/
private deferStop?: boolean
onStop?: () => void onStop?: () => void
// dev only // dev only
@ -114,11 +118,18 @@ export class ReactiveEffect<T = any> {
activeEffect = this.parent activeEffect = this.parent
shouldTrack = lastShouldTrack shouldTrack = lastShouldTrack
this.parent = undefined this.parent = undefined
if (this.deferStop) {
this.stop()
}
} }
} }
stop() { stop() {
if (this.active) { // stopped while running itself - defer the cleanup
if (activeEffect === this) {
this.deferStop = true
} else if (this.active) {
cleanupEffect(this) cleanupEffect(this)
if (this.onStop) { if (this.onStop) {
this.onStop() this.onStop()