expect(fnSpy).toBeCalledTimes(2)
})
- it('should mark dirty as MaybeDirty_ComputedSideEffect_Origin', () => {
- const v = ref(1)
- const c = computed(() => {
- v.value += 1
- return v.value
- })
-
- c.value
- expect(c.effect._dirtyLevel).toBe(
- DirtyLevels.MaybeDirty_ComputedSideEffect_Origin,
- )
- expect(COMPUTED_SIDE_EFFECT_WARN).toHaveBeenWarned()
- })
-
- it('should not infinite re-run effect when effect access original side effect computed', async () => {
- const spy = vi.fn()
- const v = ref(0)
- const c = computed(() => {
- v.value += 1
- return v.value
- })
- const Comp = {
- setup: () => {
- return () => {
- spy()
- return v.value + c.value
- }
- },
- }
- const root = nodeOps.createElement('div')
-
- render(h(Comp), root)
- expect(spy).toBeCalledTimes(1)
- await nextTick()
- expect(c.effect._dirtyLevel).toBe(
- DirtyLevels.MaybeDirty_ComputedSideEffect_Origin,
- )
- expect(serializeInner(root)).toBe('2')
- expect(COMPUTED_SIDE_EFFECT_WARN).toHaveBeenWarned()
- })
-
- it('should not infinite re-run effect when effect access chained side effect computed', async () => {
- const spy = vi.fn()
- const v = ref(0)
- const c1 = computed(() => {
- v.value += 1
- return v.value
- })
- const c2 = computed(() => v.value + c1.value)
- const Comp = {
- setup: () => {
- return () => {
- spy()
- return v.value + c1.value + c2.value
- }
- },
- }
- const root = nodeOps.createElement('div')
-
- render(h(Comp), root)
- expect(spy).toBeCalledTimes(1)
- await nextTick()
- expect(c1.effect._dirtyLevel).toBe(
- DirtyLevels.MaybeDirty_ComputedSideEffect_Origin,
- )
- expect(c2.effect._dirtyLevel).toBe(
- DirtyLevels.MaybeDirty_ComputedSideEffect,
- )
- expect(serializeInner(root)).toBe('4')
- expect(COMPUTED_SIDE_EFFECT_WARN).toHaveBeenWarned()
- })
-
it('should chained recurse effects clear dirty after trigger', () => {
const v = ref(1)
const c1 = computed(() => v.value)
c3.value
- expect(c1.effect._dirtyLevel).toBe(
- DirtyLevels.MaybeDirty_ComputedSideEffect_Origin,
- )
+ expect(c1.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
expect(c2.effect._dirtyLevel).toBe(
DirtyLevels.MaybeDirty_ComputedSideEffect,
)
})
const c2 = computed(() => v.value + c1.value)
expect(c2.value).toBe('0foo')
- expect(c2.effect._dirtyLevel).toBe(
- DirtyLevels.MaybeDirty_ComputedSideEffect,
- )
+ expect(c2.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
expect(c2.value).toBe('1foo')
expect(COMPUTED_SIDE_EFFECT_WARN).toHaveBeenWarned()
})
c2.value
})
expect(fnSpy).toBeCalledTimes(1)
- expect(c1.effect._dirtyLevel).toBe(
- DirtyLevels.MaybeDirty_ComputedSideEffect_Origin,
- )
- expect(c2.effect._dirtyLevel).toBe(
- DirtyLevels.MaybeDirty_ComputedSideEffect,
- )
+ expect(c1.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
+ expect(c2.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
v.value = 2
expect(fnSpy).toBeCalledTimes(2)
expect(COMPUTED_SIDE_EFFECT_WARN).toHaveBeenWarned()
expect(c3.effect._dirtyLevel).toBe(DirtyLevels.MaybeDirty)
c3.value
- expect(c1.effect._dirtyLevel).toBe(
- DirtyLevels.MaybeDirty_ComputedSideEffect_Origin,
- )
+ expect(c1.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
expect(c2.effect._dirtyLevel).toBe(
DirtyLevels.MaybeDirty_ComputedSideEffect,
)
render(h(Comp), root)
await nextTick()
- expect(c.effect._dirtyLevel).toBe(
- DirtyLevels.MaybeDirty_ComputedSideEffect_Origin,
- )
expect(serializeInner(root)).toBe('Hello World')
v.value += ' World'
- expect(c.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
await nextTick()
- expect(c.effect._dirtyLevel).toBe(
- DirtyLevels.MaybeDirty_ComputedSideEffect_Origin,
- )
- expect(serializeInner(root)).toBe('Hello World World World')
+ expect(serializeInner(root)).toBe('Hello World World World World')
expect(COMPUTED_SIDE_EFFECT_WARN).toHaveBeenWarned()
})
}
public get dirty() {
- // treat original side effect computed as not dirty to avoid infinite loop
- if (this._dirtyLevel === DirtyLevels.MaybeDirty_ComputedSideEffect_Origin)
- return false
if (
this._dirtyLevel === DirtyLevels.MaybeDirty_ComputedSideEffect ||
this._dirtyLevel === DirtyLevels.MaybeDirty
for (let i = 0; i < this._depsLength; i++) {
const dep = this.deps[i]
if (dep.computed) {
- // treat chained side effect computed as dirty to force it re-run
- // since we know the original side effect computed is dirty
- if (
- dep.computed.effect._dirtyLevel ===
- DirtyLevels.MaybeDirty_ComputedSideEffect_Origin
- )
- return true
triggerComputed(dep.computed)
if (this._dirtyLevel >= DirtyLevels.Dirty) {
break
for (const effect of dep.keys()) {
// dep.get(effect) is very expensive, we need to calculate it lazily and reuse the result
let tracking: boolean | undefined
-
- if (!dep.computed && effect.computed) {
- if (
- effect._runnings > 0 &&
- (tracking ??= dep.get(effect) === effect._trackId)
- ) {
- effect._dirtyLevel = DirtyLevels.MaybeDirty_ComputedSideEffect_Origin
- continue
- }
- }
-
if (
effect._dirtyLevel < dirtyLevel &&
(tracking ??= dep.get(effect) === effect._trackId)
) {
effect._shouldSchedule ||= effect._dirtyLevel === DirtyLevels.NotDirty
- // always schedule if the computed is original side effect
- // since we know it is actually dirty
- if (
- effect.computed &&
- effect._dirtyLevel === DirtyLevels.MaybeDirty_ComputedSideEffect_Origin
- ) {
- effect._shouldSchedule = true
- }
effect._dirtyLevel = dirtyLevel
}
if (