Subscribe on changes!

Watch triggered even if value is not changed

avatar
Sep 25th 2020

Version

3.0.0

Reproduction link

https://jsfiddle.net/mravLjd6/4/

Steps to reproduce

Open console and watch output

What is expected?

Only one log message "true false"

What is actually happening?

Log fired every second with "false false"


const { computed, watch, ref } = Vue;

const source = ref(0);
const isZero = computed(() => source.value === 0);

watch(isZero, (prev, next) => console.log(prev, next)); // fires every second

setInterval(() => source.value++, 1000);

This is part from docs(https://v3.vuejs.org/guide/reactivity-computed-watchers.html#watch):

The watch API is the exact equivalent of the component watch property. watch requires watching a specific data source and applies side effects in a separate callback function. It also is lazy by default - i.e. the callback is only called when the watched source has changed.

avatar
Sep 25th 2020

I think this is intended as it allows to trigger a watcher whenever a computed is reevaluated. You can still only pay attention to the value by doing:

watch(() => isOdd.value, (prev, next) => console.log(prev, next));
avatar
Sep 25th 2020

Oh, i get it. Thank you, it may be closed then

It makes more sense to me that the watch is triggeret only two times, one with (true, true) and (true, false). Why is it implemented this way?. In other words, watch does not perform any comparation in the values?

avatar
Sep 28th 2020

The same behaviour with watchEffect

const { computed, watchEffect, ref } = Vue;

const source = ref(0);
const isZero = computed(() => source.value === 0);

watchEffect(() => console.log(isZero.value)); // fires every second

setInterval(() => source.value++, 1000);
avatar
Sep 28th 2020

with watchEffect() it's expected. Since computed properties are evaluated lazily(*), the effect has to run in order to determine whether or not the value of isZero did change.

However for the watch(), we could check that the old & the new value are in fact different before calling the callback function.


  • Lazy update: Whenever a dependency of a computed property changes, the computed property is marked as dirty but not evaluated straight away. It is only run when it is actually accessed
avatar
Sep 30th 2020

https://github.com/vuejs/vue-next/blob/5d825f318f1c3467dd530e43b09040d9f8793cce/packages/runtime-core/src/apiWatch.ts#L245

Here we basically assume that if it's a ref, the value definitely has changed and we can call the callback.

The issue is that a computed ref also counts as a ref, but it doesn't neccessarily have a changed value.

So I think we can simply remove the || isRefSource condition here, that should fix it.