]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(reactivity): fix side effect computed dirty level (#11183)
authorDoctor Wu <doctorwu@moego.pet>
Sat, 22 Jun 2024 08:27:28 +0000 (16:27 +0800)
committerGitHub <noreply@github.com>
Sat, 22 Jun 2024 08:27:28 +0000 (16:27 +0800)
close #11181, #11169

packages/reactivity/__tests__/computed.spec.ts
packages/reactivity/src/computed.ts
packages/reactivity/src/effect.ts

index 0122f4e439181825064c2284b0ab6bc92fd4d3ff..9a91eed638924ecc8f5d3e72c167a8bd2b2c14bd 100644 (file)
@@ -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) => {
index 91eac36012f7afc7a09d716ed43a131c8dc925bc..e764b60248ccb455d07d8ad2227325e43b9d72b7 100644 (file)
@@ -71,11 +71,15 @@ export class ComputedRefImpl<T> {
   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 (
index 727513fc66c19b8afd73457d29973f1a129b1b12..8eb1abdab255c0eeb417bcc3e909c11c7697f290 100644 (file)
@@ -93,8 +93,10 @@ export class ReactiveEffect<T = any> {
           if (
             dep.computed.effect._dirtyLevel ===
             DirtyLevels.MaybeDirty_ComputedSideEffect_Origin
-          )
+          ) {
+            resetTracking()
             return true
+          }
           triggerComputed(dep.computed)
           if (this._dirtyLevel >= DirtyLevels.Dirty) {
             break