fix(reactivity): should not trigger watch on computed ref when value is unchanged
fix #2231
This commit is contained in:
parent
a66e53a24f
commit
390589ec6d
@ -7,13 +7,17 @@ import { CollectionTypes } from './collectionHandlers'
|
||||
declare const RefSymbol: unique symbol
|
||||
|
||||
export interface Ref<T = any> {
|
||||
value: T
|
||||
/**
|
||||
* Type differentiator only.
|
||||
* We need this to be in public d.ts but don't want it to show up in IDE
|
||||
* autocomplete, so we use a private Symbol instead.
|
||||
*/
|
||||
[RefSymbol]: true
|
||||
value: T
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_shallow?: boolean
|
||||
}
|
||||
|
||||
export type ToRefs<T = any> = { [K in keyof T]: Ref<T[K]> }
|
||||
@ -49,7 +53,7 @@ class RefImpl<T> {
|
||||
|
||||
public readonly __v_isRef = true
|
||||
|
||||
constructor(private _rawValue: T, private readonly _shallow = false) {
|
||||
constructor(private _rawValue: T, public readonly _shallow = false) {
|
||||
this._value = _shallow ? _rawValue : convert(_rawValue)
|
||||
}
|
||||
|
||||
@ -75,7 +79,7 @@ function createRef(rawValue: unknown, shallow = false) {
|
||||
}
|
||||
|
||||
export function triggerRef(ref: Ref) {
|
||||
trigger(ref, TriggerOpTypes.SET, 'value', __DEV__ ? ref.value : void 0)
|
||||
trigger(toRaw(ref), TriggerOpTypes.SET, 'value', __DEV__ ? ref.value : void 0)
|
||||
}
|
||||
|
||||
export function unref<T>(ref: T): T extends Ref<infer V> ? V : T {
|
||||
|
@ -13,7 +13,8 @@ import {
|
||||
DebuggerEvent,
|
||||
TrackOpTypes,
|
||||
TriggerOpTypes,
|
||||
triggerRef
|
||||
triggerRef,
|
||||
shallowRef
|
||||
} from '@vue/reactivity'
|
||||
|
||||
// reference: https://vue-composition-api-rfc.netlify.com/api.html#watch
|
||||
@ -750,8 +751,8 @@ describe('api: watch', () => {
|
||||
expect(calls).toBe(1)
|
||||
})
|
||||
|
||||
test('should force trigger on triggerRef when watching a ref', async () => {
|
||||
const v = ref({ a: 1 })
|
||||
test('should force trigger on triggerRef when watching a shallow ref', async () => {
|
||||
const v = shallowRef({ a: 1 })
|
||||
let sideEffect = 0
|
||||
watch(v, obj => {
|
||||
sideEffect = obj.a
|
||||
@ -785,4 +786,17 @@ describe('api: watch', () => {
|
||||
await nextTick()
|
||||
expect(spy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
// #2231
|
||||
test('computed refs should not trigger watch if value has no change', async () => {
|
||||
const spy = jest.fn()
|
||||
const source = ref(0)
|
||||
const price = computed(() => source.value === 0)
|
||||
watch(price, spy)
|
||||
source.value++
|
||||
await nextTick()
|
||||
source.value++
|
||||
await nextTick()
|
||||
expect(spy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
@ -161,9 +161,10 @@ function doWatch(
|
||||
}
|
||||
|
||||
let getter: () => any
|
||||
const isRefSource = isRef(source)
|
||||
if (isRefSource) {
|
||||
let forceTrigger = false
|
||||
if (isRef(source)) {
|
||||
getter = () => (source as Ref).value
|
||||
forceTrigger = !!(source as Ref)._shallow
|
||||
} else if (isReactive(source)) {
|
||||
getter = () => source
|
||||
deep = true
|
||||
@ -242,7 +243,7 @@ function doWatch(
|
||||
if (cb) {
|
||||
// watch(source, cb)
|
||||
const newValue = runner()
|
||||
if (deep || isRefSource || hasChanged(newValue, oldValue)) {
|
||||
if (deep || forceTrigger || hasChanged(newValue, oldValue)) {
|
||||
// cleanup before running cb again
|
||||
if (cleanup) {
|
||||
cleanup()
|
||||
|
Loading…
Reference in New Issue
Block a user