From 2a88e716000580d012c990eace830183042526cb Mon Sep 17 00:00:00 2001 From: Caleb Hearon Date: Thu, 27 Jan 2022 02:49:34 -0500 Subject: [PATCH] fix(shallowReactive): don't trigger watchers for oldVal === newVal (#894) Co-authored-by: Anthony Fu --- src/reactivity/force.ts | 9 +++++++++ src/reactivity/reactive.ts | 3 +++ src/reactivity/ref.ts | 3 +++ test/v3/reactivity/ref.spec.ts | 20 +++++++++++++++++++- 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/reactivity/force.ts diff --git a/src/reactivity/force.ts b/src/reactivity/force.ts new file mode 100644 index 00000000..727e0f5f --- /dev/null +++ b/src/reactivity/force.ts @@ -0,0 +1,9 @@ +let _isForceTrigger = false + +export function isForceTrigger() { + return _isForceTrigger +} + +export function setForceTrigger(v: boolean) { + _isForceTrigger = v +} diff --git a/src/reactivity/reactive.ts b/src/reactivity/reactive.ts index b82af25f..b8dff250 100644 --- a/src/reactivity/reactive.ts +++ b/src/reactivity/reactive.ts @@ -14,6 +14,7 @@ import { isComponentInstance, defineComponentInstance } from '../utils/helper' import { RefKey } from '../utils/symbols' import { isRef, UnwrapRef } from './ref' import { rawSet, accessModifiedSet } from '../utils/sets' +import { isForceTrigger } from './force' export function isRaw(obj: any): boolean { return Boolean( @@ -213,6 +214,8 @@ export function shallowReactive(obj: any) { }, set: function setterHandler(newVal: any) { if (getter && !setter) return + const value = getter ? getter.call(obj) : val + if (!isForceTrigger() && value === newVal) return if (setter) { setter.call(obj, newVal) } else { diff --git a/src/reactivity/ref.ts b/src/reactivity/ref.ts index 044a63df..4da6a55f 100644 --- a/src/reactivity/ref.ts +++ b/src/reactivity/ref.ts @@ -3,6 +3,7 @@ import { proxy, isPlainObject, warn, def } from '../utils' import { reactive, isReactive, shallowReactive } from './reactive' import { readonlySet } from '../utils/sets' import { set } from './set' +import { setForceTrigger } from './force' declare const _refBrand: unique symbol export interface Ref { @@ -184,7 +185,9 @@ export function shallowRef(raw?: unknown) { export function triggerRef(value: any) { if (!isRef(value)) return + setForceTrigger(true) value.value = value.value + setForceTrigger(false) } export function proxyRefs( diff --git a/test/v3/reactivity/ref.spec.ts b/test/v3/reactivity/ref.spec.ts index b4f9c958..1443f922 100644 --- a/test/v3/reactivity/ref.spec.ts +++ b/test/v3/reactivity/ref.spec.ts @@ -250,11 +250,29 @@ describe('reactivity/ref', () => { expect(dummy).toBe(1) // should not trigger yet // force trigger - // sref.value = sref.value; triggerRef(sref) expect(dummy).toBe(2) }) + test('shallowRef noop when assignment is the same', () => { + const sref = shallowRef(1) + let calls = 0 + watchEffect( + () => { + sref.value + calls++ + }, + { flush: 'sync' } + ) + expect(calls).toBe(1) + + sref.value = 2 + expect(calls).toBe(2) + + sref.value = 2 + expect(calls).toBe(2) + }) + test('isRef', () => { expect(isRef(ref(1))).toBe(true) expect(isRef(computed(() => 1))).toBe(true)