computed callback is triggered unexpectedly
Vue version
3.3.4
Link to minimal reproduction
Steps to reproduce
- Make a computed variable always return the same value whatever its dependency changes.
- 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.
- 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
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.
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.
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.
related (i guess) https://github.com/vuejs/core/pull/5912
Thank you so much LinusBorg, I was just reading your article: Vue: When a computed property can be the wrong tool