From 716275d1b1d2383d8ef0306fcd94558d4d9170f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Lehni?= Date: Thu, 5 Sep 2024 12:54:30 +0200 Subject: [PATCH] fix(reactivity): prevent endless recursion in computed getters (#11797) --- .../reactivity/__tests__/computed.spec.ts | 45 +++++++++++++++++++ packages/reactivity/src/dep.ts | 2 +- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index c3409eee77e..31daef559a8 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, @@ -958,4 +961,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 }