Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Watch triggered even if value is not changed #2231

Closed
edikdeisling opened this issue Sep 25, 2020 · 6 comments
Closed

Watch triggered even if value is not changed #2231

edikdeisling opened this issue Sep 25, 2020 · 6 comments
Labels
has PR A pull request has already been submitted to solve the issue has workaround A workaround has been found to avoid the problem 🐞 bug Something isn't working

Comments

@edikdeisling
Copy link

edikdeisling commented Sep 25, 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.

@posva
Copy link
Member

posva commented Sep 25, 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));

@edikdeisling
Copy link
Author

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

@Jorge-Luis-Rangel-Peralta
Copy link

Jorge-Luis-Rangel-Peralta commented Sep 26, 2020

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?

@edikdeisling
Copy link
Author

edikdeisling commented Sep 28, 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);

@LinusBorg
Copy link
Member

LinusBorg commented Sep 28, 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

@LinusBorg
Copy link
Member

LinusBorg commented Sep 30, 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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
has PR A pull request has already been submitted to solve the issue has workaround A workaround has been found to avoid the problem 🐞 bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants