diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index 08b034f79f1..3beb1bba253 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -1,4 +1,6 @@ import { + type TestElement, + defineComponent, h, nextTick, nodeOps, @@ -6,6 +8,7 @@ import { onUnmounted, render, serializeInner, + triggerEvent, } from '@vue/runtime-test' import { type DebuggerEvent, @@ -944,4 +947,46 @@ describe('reactivity/computed', () => { newValue: 2, }) }) + + test('should prevent endless recursion in self-referencing computed getters', async () => { + const Comp = defineComponent({ + data() { + return { + counter: 0, + } + }, + + computed: { + message(): string { + if (this.counter === 0) { + this.counter++ + return this.message + } else { + return `Step ${this.counter}` + } + }, + }, + + render() { + return [ + h( + 'button', + { + onClick: () => { + this.counter++ + }, + }, + 'Step', + ), + h('p', this.message), + ] + }, + }) + const root = nodeOps.createElement('div') + render(h(Comp), root) + expect(serializeInner(root)).toBe(`
`) + triggerEvent(root.children[1] as TestElement, 'click') + await nextTick() + expect(serializeInner(root)).toBe(`Step 2
`) + }) }) diff --git a/packages/reactivity/src/dep.ts b/packages/reactivity/src/dep.ts index 4ce73ac9954..6d938cbc25f 100644 --- a/packages/reactivity/src/dep.ts +++ b/packages/reactivity/src/dep.ts @@ -46,7 +46,7 @@ export class Dep { } track(debugInfo?: DebuggerEventExtraInfo): Link | undefined { - if (!activeSub || !shouldTrack) { + if (!activeSub || !shouldTrack || activeSub === this.computed) { return }