From f0605a8e8a9f8616b88db2ffbe86f9acc2e94b2c Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Tue, 26 Jan 2021 14:38:33 +0800 Subject: [PATCH 1/4] test: add failed test for #498 --- test/v3/runtime-core/apiWatch.spec.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/v3/runtime-core/apiWatch.spec.ts b/test/v3/runtime-core/apiWatch.spec.ts index b98d95b3..3e3510e9 100644 --- a/test/v3/runtime-core/apiWatch.spec.ts +++ b/test/v3/runtime-core/apiWatch.spec.ts @@ -531,4 +531,30 @@ 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) + + state.data = [] + + await nextTick() + + expect(dummy).toBe(2) + }) }) From a405fc86ef9b6ffb64393dffc91ea59a5c4cfbf4 Mon Sep 17 00:00:00 2001 From: Caleb Hearon Date: Fri, 13 Aug 2021 14:54:59 -0400 Subject: [PATCH 2/4] fix: don't invoke Vue getters in setter Fixes #498 --- src/reactivity/reactive.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 } From a116c34785da6ccf4df5ee9dd5d89d81e77dbf73 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sat, 14 Aug 2021 10:17:29 +0800 Subject: [PATCH 3/4] Restore defineAsyncComponent.ts --- src/component/defineAsyncComponent.ts | 71 +++++++++++++-------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/src/component/defineAsyncComponent.ts b/src/component/defineAsyncComponent.ts index e79fef02..9662a2c2 100644 --- a/src/component/defineAsyncComponent.ts +++ b/src/component/defineAsyncComponent.ts @@ -74,42 +74,41 @@ export function defineAsyncComponent( let thisRequest: Promise return ( pendingRequest || - (thisRequest = pendingRequest = - loader() - .catch((err) => { - err = err instanceof Error ? err : new Error(String(err)) - if (userOnError) { - return new Promise((resolve, reject) => { - const userRetry = () => resolve(retry()) - const userFail = () => reject(err) - userOnError(err, userRetry, userFail, retries + 1) - }) - } else { - throw err - } - }) - .then((comp: any) => { - if (thisRequest !== pendingRequest && pendingRequest) { - return pendingRequest - } - if (__DEV__ && !comp) { - warn( - `Async component loader resolved to undefined. ` + - `If you are using retry(), make sure to return its return value.` - ) - } - // interop module default - if ( - comp && - (comp.__esModule || comp[Symbol.toStringTag] === 'Module') - ) { - comp = comp.default - } - if (__DEV__ && comp && !isObject(comp) && !isFunction(comp)) { - throw new Error(`Invalid async component load result: ${comp}`) - } - return comp - })) + (thisRequest = pendingRequest = loader() + .catch((err) => { + err = err instanceof Error ? err : new Error(String(err)) + if (userOnError) { + return new Promise((resolve, reject) => { + const userRetry = () => resolve(retry()) + const userFail = () => reject(err) + userOnError(err, userRetry, userFail, retries + 1) + }) + } else { + throw err + } + }) + .then((comp: any) => { + if (thisRequest !== pendingRequest && pendingRequest) { + return pendingRequest + } + if (__DEV__ && !comp) { + warn( + `Async component loader resolved to undefined. ` + + `If you are using retry(), make sure to return its return value.` + ) + } + // interop module default + if ( + comp && + (comp.__esModule || comp[Symbol.toStringTag] === 'Module') + ) { + comp = comp.default + } + if (__DEV__ && comp && !isObject(comp) && !isFunction(comp)) { + throw new Error(`Invalid async component load result: ${comp}`) + } + return comp + })) ) } From f16ea0cb61d642229b6b225b0be3f011cbc590e2 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sat, 14 Aug 2021 10:19:47 +0800 Subject: [PATCH 4/4] chore: update test --- test/v3/runtime-core/apiWatch.spec.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/v3/runtime-core/apiWatch.spec.ts b/test/v3/runtime-core/apiWatch.spec.ts index 24865d74..d1f440a1 100644 --- a/test/v3/runtime-core/apiWatch.spec.ts +++ b/test/v3/runtime-core/apiWatch.spec.ts @@ -552,12 +552,6 @@ describe('api: watch', () => { expect(dummy).toBe(1) await nextTick() expect(dummy).toBe(1) - - state.data = [] - - await nextTick() - - expect(dummy).toBe(2) }) it('watching deep ref', async () => {