From 3bd79e3e5ed960fc42cbf77bc61a97d2c03557c0 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Sat, 22 Jun 2024 16:27:28 +0800 Subject: [PATCH] fix(reactivity): fix side effect computed dirty level (#11183) close #11181, #11169 --- .../reactivity/__tests__/computed.spec.ts | 57 +++++++++++++++++++ packages/reactivity/src/computed.ts | 6 +- packages/reactivity/src/effect.ts | 4 +- 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index 0122f4e4391..9a91eed6389 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -708,6 +708,63 @@ describe('reactivity/computed', () => { expect(COMPUTED_SIDE_EFFECT_WARN).toHaveBeenWarned() }) + it('should chained computeds keep reactivity when computed effect happens', async () => { + const v = ref('Hello') + const c = computed(() => { + v.value += ' World' + return v.value + }) + const d = computed(() => c.value) + const e = computed(() => d.value) + const Comp = { + setup: () => { + return () => d.value + ' | ' + e.value + }, + } + const root = nodeOps.createElement('div') + + render(h(Comp), root) + await nextTick() + expect(serializeInner(root)).toBe('Hello World | Hello World') + + v.value += ' World' + await nextTick() + expect(serializeInner(root)).toBe( + 'Hello World World World | Hello World World World', + ) + expect(COMPUTED_SIDE_EFFECT_WARN).toHaveBeenWarned() + }) + + it('should keep dirty level when side effect computed value changed', () => { + const v = ref(0) + const c = computed(() => { + v.value += 1 + return v.value + }) + const d = computed(() => { + return { d: c.value } + }) + + const Comp = { + setup: () => { + return () => { + return [d.value.d, d.value.d] + } + }, + } + + const root = nodeOps.createElement('div') + render(h(Comp), root) + + expect(d.value.d).toBe(1) + expect(serializeInner(root)).toBe('11') + expect(c.effect._dirtyLevel).toBe( + DirtyLevels.MaybeDirty_ComputedSideEffect_Origin, + ) + expect(d.effect._dirtyLevel).toBe(DirtyLevels.MaybeDirty_ComputedSideEffect) + expect(COMPUTED_SIDE_EFFECT_WARN).toHaveBeenWarned() + }) + it('debug: onTrigger (ref)', () => { let events: DebuggerEvent[] = [] const onTrigger = vi.fn((e: DebuggerEvent) => { diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 91eac36012f..e764b60248c 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -71,11 +71,15 @@ export class ComputedRefImpl { get value() { // the computed ref may get wrapped by other proxies e.g. readonly() #3376 const self = toRaw(this) + const lastDirtyLevel = self.effect._dirtyLevel if ( (!self._cacheable || self.effect.dirty) && hasChanged(self._value, (self._value = self.effect.run()!)) ) { - triggerRefValue(self, DirtyLevels.Dirty) + // keep dirty level when side effect computed's value changed + if (lastDirtyLevel !== DirtyLevels.MaybeDirty_ComputedSideEffect) { + triggerRefValue(self, DirtyLevels.Dirty) + } } trackRefValue(self) if ( diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 727513fc66c..8eb1abdab25 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -93,8 +93,10 @@ export class ReactiveEffect { if ( dep.computed.effect._dirtyLevel === DirtyLevels.MaybeDirty_ComputedSideEffect_Origin - ) + ) { + resetTracking() return true + } triggerComputed(dep.computed) if (this._dirtyLevel >= DirtyLevels.Dirty) { break