feat(reactivity): new effectScope API (#2195)
This commit is contained in:
238
packages/reactivity/__tests__/effectScope.spec.ts
Normal file
238
packages/reactivity/__tests__/effectScope.spec.ts
Normal file
@@ -0,0 +1,238 @@
|
||||
import { nextTick, watch, watchEffect } from '@vue/runtime-core'
|
||||
import {
|
||||
reactive,
|
||||
effect,
|
||||
EffectScope,
|
||||
onScopeDispose,
|
||||
computed,
|
||||
ref,
|
||||
ComputedRef
|
||||
} from '../src'
|
||||
|
||||
describe('reactivity/effect/scope', () => {
|
||||
it('should run', () => {
|
||||
const fnSpy = jest.fn(() => {})
|
||||
new EffectScope().run(fnSpy)
|
||||
expect(fnSpy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should accept zero argument', () => {
|
||||
const scope = new EffectScope()
|
||||
expect(scope.effects.length).toBe(0)
|
||||
})
|
||||
|
||||
it('should return run value', () => {
|
||||
expect(new EffectScope().run(() => 1)).toBe(1)
|
||||
})
|
||||
|
||||
it('should collect the effects', () => {
|
||||
const scope = new EffectScope()
|
||||
scope.run(() => {
|
||||
let dummy
|
||||
const counter = reactive({ num: 0 })
|
||||
effect(() => (dummy = counter.num))
|
||||
|
||||
expect(dummy).toBe(0)
|
||||
counter.num = 7
|
||||
expect(dummy).toBe(7)
|
||||
})
|
||||
|
||||
expect(scope.effects.length).toBe(1)
|
||||
})
|
||||
|
||||
it('stop', () => {
|
||||
let dummy, doubled
|
||||
const counter = reactive({ num: 0 })
|
||||
|
||||
const scope = new EffectScope()
|
||||
scope.run(() => {
|
||||
effect(() => (dummy = counter.num))
|
||||
effect(() => (doubled = counter.num * 2))
|
||||
})
|
||||
|
||||
expect(scope.effects.length).toBe(2)
|
||||
|
||||
expect(dummy).toBe(0)
|
||||
counter.num = 7
|
||||
expect(dummy).toBe(7)
|
||||
expect(doubled).toBe(14)
|
||||
|
||||
scope.stop()
|
||||
|
||||
counter.num = 6
|
||||
expect(dummy).toBe(7)
|
||||
expect(doubled).toBe(14)
|
||||
})
|
||||
|
||||
it('should collect nested scope', () => {
|
||||
let dummy, doubled
|
||||
const counter = reactive({ num: 0 })
|
||||
|
||||
const scope = new EffectScope()
|
||||
scope.run(() => {
|
||||
effect(() => (dummy = counter.num))
|
||||
// nested scope
|
||||
new EffectScope().run(() => {
|
||||
effect(() => (doubled = counter.num * 2))
|
||||
})
|
||||
})
|
||||
|
||||
expect(scope.effects.length).toBe(2)
|
||||
expect(scope.effects[1]).toBeInstanceOf(EffectScope)
|
||||
|
||||
expect(dummy).toBe(0)
|
||||
counter.num = 7
|
||||
expect(dummy).toBe(7)
|
||||
expect(doubled).toBe(14)
|
||||
|
||||
// stop the nested scope as well
|
||||
scope.stop()
|
||||
|
||||
counter.num = 6
|
||||
expect(dummy).toBe(7)
|
||||
expect(doubled).toBe(14)
|
||||
})
|
||||
|
||||
it('nested scope can be escaped', () => {
|
||||
let dummy, doubled
|
||||
const counter = reactive({ num: 0 })
|
||||
|
||||
const scope = new EffectScope()
|
||||
scope.run(() => {
|
||||
effect(() => (dummy = counter.num))
|
||||
// nested scope
|
||||
new EffectScope(true).run(() => {
|
||||
effect(() => (doubled = counter.num * 2))
|
||||
})
|
||||
})
|
||||
|
||||
expect(scope.effects.length).toBe(1)
|
||||
|
||||
expect(dummy).toBe(0)
|
||||
counter.num = 7
|
||||
expect(dummy).toBe(7)
|
||||
expect(doubled).toBe(14)
|
||||
|
||||
scope.stop()
|
||||
|
||||
counter.num = 6
|
||||
expect(dummy).toBe(7)
|
||||
|
||||
// nested scope should not be stoped
|
||||
expect(doubled).toBe(12)
|
||||
})
|
||||
|
||||
it('able to run the scope', () => {
|
||||
let dummy, doubled
|
||||
const counter = reactive({ num: 0 })
|
||||
|
||||
const scope = new EffectScope()
|
||||
scope.run(() => {
|
||||
effect(() => (dummy = counter.num))
|
||||
})
|
||||
|
||||
expect(scope.effects.length).toBe(1)
|
||||
|
||||
scope.run(() => {
|
||||
effect(() => (doubled = counter.num * 2))
|
||||
})
|
||||
|
||||
expect(scope.effects.length).toBe(2)
|
||||
|
||||
counter.num = 7
|
||||
expect(dummy).toBe(7)
|
||||
expect(doubled).toBe(14)
|
||||
|
||||
scope.stop()
|
||||
})
|
||||
|
||||
it('can not run an inactive scope', () => {
|
||||
let dummy, doubled
|
||||
const counter = reactive({ num: 0 })
|
||||
|
||||
const scope = new EffectScope()
|
||||
scope.run(() => {
|
||||
effect(() => (dummy = counter.num))
|
||||
})
|
||||
|
||||
expect(scope.effects.length).toBe(1)
|
||||
|
||||
scope.stop()
|
||||
|
||||
scope.run(() => {
|
||||
effect(() => (doubled = counter.num * 2))
|
||||
})
|
||||
|
||||
expect('[Vue warn] cannot run an inactive effect scope.').toHaveBeenWarned()
|
||||
|
||||
expect(scope.effects.length).toBe(1)
|
||||
|
||||
counter.num = 7
|
||||
expect(dummy).toBe(0)
|
||||
expect(doubled).toBe(undefined)
|
||||
})
|
||||
|
||||
it('should fire onDispose hook', () => {
|
||||
let dummy = 0
|
||||
|
||||
const scope = new EffectScope()
|
||||
scope.run(() => {
|
||||
onScopeDispose(() => (dummy += 1))
|
||||
onScopeDispose(() => (dummy += 2))
|
||||
})
|
||||
|
||||
scope.run(() => {
|
||||
onScopeDispose(() => (dummy += 4))
|
||||
})
|
||||
|
||||
expect(dummy).toBe(0)
|
||||
|
||||
scope.stop()
|
||||
expect(dummy).toBe(7)
|
||||
})
|
||||
|
||||
it('test with higher level APIs', async () => {
|
||||
const r = ref(1)
|
||||
|
||||
const computedSpy = jest.fn()
|
||||
const watchSpy = jest.fn()
|
||||
const watchEffectSpy = jest.fn()
|
||||
|
||||
let c: ComputedRef
|
||||
const scope = new EffectScope()
|
||||
scope.run(() => {
|
||||
c = computed(() => {
|
||||
computedSpy()
|
||||
return r.value + 1
|
||||
})
|
||||
|
||||
watch(r, watchSpy)
|
||||
watchEffect(() => {
|
||||
watchEffectSpy()
|
||||
r.value
|
||||
})
|
||||
})
|
||||
|
||||
c!.value // computed is lazy so trigger collection
|
||||
expect(computedSpy).toHaveBeenCalledTimes(1)
|
||||
expect(watchSpy).toHaveBeenCalledTimes(0)
|
||||
expect(watchEffectSpy).toHaveBeenCalledTimes(1)
|
||||
|
||||
r.value++
|
||||
c!.value
|
||||
await nextTick()
|
||||
expect(computedSpy).toHaveBeenCalledTimes(2)
|
||||
expect(watchSpy).toHaveBeenCalledTimes(1)
|
||||
expect(watchEffectSpy).toHaveBeenCalledTimes(2)
|
||||
|
||||
scope.stop()
|
||||
|
||||
r.value++
|
||||
c!.value
|
||||
await nextTick()
|
||||
// should not trigger anymore
|
||||
expect(computedSpy).toHaveBeenCalledTimes(2)
|
||||
expect(watchSpy).toHaveBeenCalledTimes(1)
|
||||
expect(watchEffectSpy).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user