diff --git a/src/reactivity/reactive.ts b/src/reactivity/reactive.ts index 7599ab68..d08b7c5b 100644 --- a/src/reactivity/reactive.ts +++ b/src/reactivity/reactive.ts @@ -94,14 +94,14 @@ export function defineAccessControl(target: AnyObject, key: any, val?: any) { set: function setterHandler(newVal: any) { if (getter && !setter) return - const value = getter ? getter.call(target) : val // If the key is equal to RefKey, skip the unwrap logic // If and only if "value" is ref and "newVal" is not a ref, // the assignment should be proxied to "value" ref. - if (key !== RefKey && isRef(value) && !isRef(newVal)) { - value.value = newVal + if (key !== RefKey && isRef(val) && !isRef(newVal)) { + val.value = newVal } else if (setter) { setter.call(target, newVal) + val = newVal } else { val = newVal } diff --git a/test/v3/runtime-core/apiWatch.spec.ts b/test/v3/runtime-core/apiWatch.spec.ts index 6fde5b24..d1f440a1 100644 --- a/test/v3/runtime-core/apiWatch.spec.ts +++ b/test/v3/runtime-core/apiWatch.spec.ts @@ -532,6 +532,28 @@ describe('api: watch', () => { expect(data2.value).toMatchObject([1]) }) + // #498 + it('watchEffect should not lead to infinite loop when accessing', async () => { + let dummy = 0 + + const state = reactive({ + data: [], + watchVar: 123, + }) + + watchEffect(() => { + // accessing + state.watchVar + // setting + state.data = [] + dummy += 1 + }) + + expect(dummy).toBe(1) + await nextTick() + expect(dummy).toBe(1) + }) + it('watching deep ref', async () => { const count = ref(0) const double = computed(() => count.value * 2)