Skip to content

Commit f7ba97f

Browse files
authoredFeb 13, 2024··
feat(dx): warn users when computed is self-triggering (#10299)
1 parent b99ac93 commit f7ba97f

File tree

2 files changed

+16
-1
lines changed

2 files changed

+16
-1
lines changed
 

‎packages/reactivity/__tests__/computed.spec.ts

+7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
toRaw,
1515
} from '../src'
1616
import { DirtyLevels } from '../src/constants'
17+
import { COMPUTED_SIDE_EFFECT_WARN } from '../src/computed'
1718

1819
describe('reactivity/computed', () => {
1920
it('should return updated value', () => {
@@ -488,6 +489,7 @@ describe('reactivity/computed', () => {
488489
expect(c3.effect._dirtyLevel).toBe(
489490
DirtyLevels.MaybeDirty_ComputedSideEffect,
490491
)
492+
expect(COMPUTED_SIDE_EFFECT_WARN).toHaveBeenWarned()
491493
})
492494

493495
it('should work when chained(ref+computed)', () => {
@@ -502,6 +504,7 @@ describe('reactivity/computed', () => {
502504
expect(c2.value).toBe('0foo')
503505
expect(c2.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
504506
expect(c2.value).toBe('1foo')
507+
expect(COMPUTED_SIDE_EFFECT_WARN).toHaveBeenWarned()
505508
})
506509

507510
it('should trigger effect even computed already dirty', () => {
@@ -524,6 +527,7 @@ describe('reactivity/computed', () => {
524527
expect(c2.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
525528
v.value = 2
526529
expect(fnSpy).toBeCalledTimes(2)
530+
expect(COMPUTED_SIDE_EFFECT_WARN).toHaveBeenWarned()
527531
})
528532

529533
// #10185
@@ -567,6 +571,7 @@ describe('reactivity/computed', () => {
567571
expect(c3.effect._dirtyLevel).toBe(DirtyLevels.MaybeDirty)
568572

569573
expect(c3.value).toBe('yes')
574+
expect(COMPUTED_SIDE_EFFECT_WARN).toHaveBeenWarned()
570575
})
571576

572577
it('should be not dirty after deps mutate (mutate deps in computed)', async () => {
@@ -588,6 +593,7 @@ describe('reactivity/computed', () => {
588593
await nextTick()
589594
await nextTick()
590595
expect(serializeInner(root)).toBe(`2`)
596+
expect(COMPUTED_SIDE_EFFECT_WARN).toHaveBeenWarned()
591597
})
592598

593599
it('should not trigger effect scheduler by recurse computed effect', async () => {
@@ -610,5 +616,6 @@ describe('reactivity/computed', () => {
610616
v.value += ' World'
611617
await nextTick()
612618
expect(serializeInner(root)).toBe('Hello World World World World')
619+
expect(COMPUTED_SIDE_EFFECT_WARN).toHaveBeenWarned()
613620
})
614621
})

‎packages/reactivity/src/computed.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { NOOP, hasChanged, isFunction } from '@vue/shared'
44
import { toRaw } from './reactive'
55
import type { Dep } from './dep'
66
import { DirtyLevels, ReactiveFlags } from './constants'
7+
import { warn } from './warning'
78

89
declare const ComputedRefSymbol: unique symbol
910

@@ -24,6 +25,12 @@ export interface WritableComputedOptions<T> {
2425
set: ComputedSetter<T>
2526
}
2627

28+
export const COMPUTED_SIDE_EFFECT_WARN =
29+
`Computed is still dirty after getter evaluation,` +
30+
` likely because a computed is mutating its own dependency in its getter.` +
31+
` State mutations in computed getters should be avoided. ` +
32+
` Check the docs for more details: https://vuejs.org/guide/essentials/computed.html#getters-should-be-side-effect-free`
33+
2734
export class ComputedRefImpl<T> {
2835
public dep?: Dep = undefined
2936

@@ -67,6 +74,7 @@ export class ComputedRefImpl<T> {
6774
}
6875
trackRefValue(self)
6976
if (self.effect._dirtyLevel >= DirtyLevels.MaybeDirty_ComputedSideEffect) {
77+
__DEV__ && warn(COMPUTED_SIDE_EFFECT_WARN)
7078
triggerRefValue(self, DirtyLevels.MaybeDirty_ComputedSideEffect)
7179
}
7280
return self._value
@@ -141,7 +149,7 @@ export function computed<T>(
141149
getter = getterOrOptions
142150
setter = __DEV__
143151
? () => {
144-
console.warn('Write operation failed: computed value is readonly')
152+
warn('Write operation failed: computed value is readonly')
145153
}
146154
: NOOP
147155
} else {

0 commit comments

Comments
 (0)
Please sign in to comment.