Subscribe on changes!

computed callback is triggered unexpectedly

avatar
May 19th 2023

Vue version

3.3.4

Link to minimal reproduction

https://play.vuejs.org/#eNptkM1ugzAQhF9l5QtQUSgcW4PUZ+ix7gGZjUHyn4yhB8S7d1NKlCY57oznm/Wu7N37YpmRvTI+yTD6CBPG2bfCjsa7EGGFgKccpDN+jtjn8N1FOcAGp+AMJJRNhBVWOjtFiGFUCgM051BaZW/CAhymrEg/OGmaQdMegWLp9IzwBC9H5LcllVUO+8MzwmkstFNpQiQ5dFZhn2Q3HfV9x6esdv7XDbx+DK//w3m5H4ZOQkNE43UXkSYAPlTtul5+vW28JOXKoUUfiHUR3QeFrKL6a3+0tDcsz8b1qBvB/sCCkc3LSzXbfgCirI+4

Steps to reproduce

  1. Make a computed variable always return the same value whatever its dependency changes.
  2. Create another computed variable which wraps the previous computed value with an array (or Object), in this way it will always return different reference (value) whenever its element changes.
  3. Watch the second computed value.

What is expected?

The second computed value shall never trigger watch callback because its dependency never changes, thus it never create a different reference.

What is actually happening?

But it triggered unexpectedly, that may lead to duplicating side effects

System Info

No response

Any additional comments?

No response

avatar
May 20th 2023

This is expected, because the trigger is determined by comparing the oldValue and the newValue. In your example, const c2 = computed(() => [c1.value]);, even though c1.value didn't change (so the first watch was not triggered), you wrapped it in an array when returning it, which always made it unequal, like [0] === [0] was always false, so the second watch was triggered.

avatar
May 22nd 2023

Thank you qijizh for answering! The point is that the first computed (c1) has not changed, it might recompute because its dependency's value has changed, but the output of the computation remains the same (0 === 0). Thus the watcher observing c1 is not triggered, but why is c2's recomputation triggered if its only dependency has not changed?

I believe the triggering rules for both 'watch' and 'computed' were identical, which means if the dependency of a watch does not trigger the watch's callback, then the same dependency will not trigger a recomputation of computed properties.

avatar
May 22nd 2023

No intention to close this

avatar
May 22nd 2023

computed's are evaluated lazily - so their new return value can only be known once something actually reads its .value.

for that to happen though, effects depending on a computed have to be triggered - so that they than in turn can read the computed's value and it be re-evaluated.

So the behavior you are seeing is to be expected.

BTW: the watch also triggers, in that the watch's getter triggers. But since the computed does return the same valze, the watch's callback can be skipped.

avatar
May 22nd 2023

Thank you so much LinusBorg, I was just reading your article: Vue: When a computed property can be the wrong tool