Interesting bug / quirk computed property not updating correctly, but probably not fixable
See original GitHub issueHi consider this snippet:
...
computed: {
someProp() {
if(this.nonReactiveProperty) {
return this.reactiveProperty;
}
}
},
...
From the documentation I would assume (please show me if I missed something) that someProp()
should get re-evaluated when this.reactiveProperty
does change. Works as expected as long as this.nonReactiveProprty
is true all time. However, if this.nonReactiveProperty
is false the first time someProp() is read, then someProp() will never re-evaluate, even if this.reactiveProperty
changes (after this.nonReactiveProperty
gets true).
The problem is, I guess it is not fixable. Am I assuming correctly, that the dependencies of someProp()
get calculated the first time it is executed? So code which does not get executed then, vue is just blind of…?
This is quite tricky and probably I’ve run into this quite some times before figuring out. It gets even harder to see when inside lazy conditions. E.g. this works all the time:
someProp() {
if(this.reactiveProperty && this.nonReactiveProperty) {
return this.reactiveProperty;
}
}
while this might not:
someProp() {
if(this.nonReactiveProperty && this.reactiveProperty) {
return this.reactiveProperty;
}
}
So if this is not fixable, and I didn’t miss something, I would suggest to add this to the documentation.
EDIT: here is a codesandbox: https://codesandbox.io/s/priceless-leftpad-kktuq?file=/src/App.vue
best Martin
Issue Analytics
- State:
- Created 3 years ago
- Comments:15 (3 by maintainers)
@derMart
EDIT: Nvm, noticed you mentioned that in the last reply. 😅 I think that behaviour could be better described in the API docs.
It’s worth adding that (as far as I remember) the dependencies are collected whenever the computed property executes without using the cache. Any dependencies collected during previous runs will also be removed.
So in case you have several conditionals:
When
this.reactiveProp === 0
istrue
, the only dependencies collected will be:this.reactiveProp
andthis.anotherReactiveProp
. Whenthis.reactiveProp === 0
isfalse
andthis.yetAnotherReactiveProp === 1
is true, the collected dependencies list will be:this.reactiveProp
andthis.yetAnotherReactiveProp
. When neither of those two conditions pass (finalelse
returns), the list of dependencies will look like this:this.reactiveProp
,this.yetAnotherReactiveProp
andthis.alsoReactiveProp
.As you can see the list of dependencies is dynamic and collected at runtime as it was already mentioned. Which means if during the last execution of that computed property something wasn’t registered as a dependency, changing it won’t trigger a recalculation. And this is ultimately a good thing if you think about it.
Here is an example codesandbox which tries to reconstruct dependency detection in more detail: https://codesandbox.io/s/nifty-darkness-fqf6x?file=/src/App.vue See the console output and comments. My conclusion of this is, that dependencies are re-tracked every time a re-evaluation of a computed property is triggered. This also means that reactive properties which once were identified dependencies might be removed from the list after a later evaluation during which they were not accessed.
Another conclusion is the workaround suggested by @jacekkarczmarczyk (with a little loss in execution performance): Just put all the needed reactive dependencies as pure statements at the beginning of the computed property, in case of the example above:
This will call their getter and mark them as dependent.