Skip to content

Commit

Permalink
fix(reactivity): should not trigger watch on computed ref when value …
Browse files Browse the repository at this point in the history
…is unchanged

fix #2231
  • Loading branch information
yyx990803 committed Oct 6, 2020
1 parent a66e53a commit 390589e
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 9 deletions.
10 changes: 7 additions & 3 deletions packages/reactivity/src/ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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]> }
Expand Down Expand Up @@ -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)
}

Expand All @@ -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 {
Expand Down
20 changes: 17 additions & 3 deletions packages/runtime-core/__tests__/apiWatch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
})
})
7 changes: 4 additions & 3 deletions packages/runtime-core/src/apiWatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 390589e

Please sign in to comment.