watcher for injected value is triggered before destroying the component displayed with `v-if="value"` after the value is set to falsy
Vue version
3.2.37
and 2.7.8
Link to minimal reproduction
Steps to reproduce
- provide a ref value with object/undefined
- display subcomponent conditionally with (
v-if="value"
) - watch some property of the injected value in the subcomponent
- set the ref to an object with the property, then after some time set the ref to undefined
What is expected?
Since the subcomponent is displayed with v-if="value" then when value changes to undefined the subcomponent should be destroyed and the watcher in the subcomponent shouldn't be triggered (and therefore it shouldn't fail)
What is actually happening?
Cannot read properties of undefined (reading 'bar')
System Info
No response
Any additional comments?
Same behaviour can be observed in 2.7 (demo), but not in 2.6 with @vue/composition-api
(demo) (that's how I discovered the problem)
in case this is not bug , you can use the watch flush:post
option
Thanks for the solution, wouldn't it be a breaking change though? I mean technically it isn't, as prior to 3.x/2.7 there was no ref()
and watch()
, but what is the reason for being it inconsistent with options API?
Working options API examples: 2.7.8, 3.2.27
And last but not least - is there any chance to make it work same like in options API or 2.6 with plugin? This behaviour makes a migration from 2.6 with plugin to 2.7/3.x harder as I have to go through every single watcher and see if it's not affected
The difference between your initial example and the options API one with3.2.27 is not in the watcher behavior. It's in the way you use provide
. (However, yes there is a difference between Vue 2 and 3 worth exploring).
Concerning 3.2
In your options API example, doing this will not trigger the watcher because this change is never being propagated to children:
this.container.foo = undefined
reason being: you used the this.container
object as the provide options object, and Vue will not update the provided values for any changes you do against that object. So from the perspective of the child, the provided value never changes and that's why the watch doesn't fire.
Change your timeout to the following to see how the change will be watched and console.log
'd event though the component will unmount:
template: '<sub-component v-if="container.foo.bar" />',
data: () => ({ container: { foo: { bar: true } } }),
created () {
setInterval(() => (this.container.foo.bar = false), 1000);
},
The same codes does not log false
in Vue 2, so that's the difference we can explore. The internal effect queue in Vue 3 works a bit differently than Vue 2, and we have had a few edge-casey differences resulting from that. Wether that is worthy of a fix / fixable, I think Evan needs to take a look at that.
The reason for 2.7 composition API being different to Vue 2 Options API but working closer to Vue 3 is just that, I presume - being closer to Vue 3.
And last but not least - is there any chance to make it work same like in options API or 2.6 with plugin? This behavior makes a migration from 2.6 with plugin to 2.7/3.x harder as I have to go through every single watcher and see if it's not affected
With a kind of global setting or something? no.
Thanks for the response, I indeed messed provides, here's updated version for 2.7 and 3.2 (and using this.$watch
: 2.7 and 3.2).
I used undefined
and { bar: true }
values for foo
as in my original examples, the result is similar to what you've mentioned - 3.2 logs undefined
, 2.7 doesn't. Options API version doesn't fail though, as I guess watching foo.bar.baz
takes possibility of some variable from the chain being undefined
into account, while in Composition API (or in Options API but with this.$watch(() => ...)
) you need to take care it yourself.
So I don't really mind Composition API being different from the Options API, the problem is that in
- 2.6 with Options API
- 2.6 with Composition
- 2.7 with Options API
component is destroyed first, and in
- 2.7 with Composition API
- 3.2 with Options API
- 3.2 with Composition API
component is destroyed after watcher is triggered with undefined
value. This might be not a big problem in the examples I've provided, worst thing that can happen is just a warning/error in the console, but I'd have to evaluate all of my >250 watchers to make sure that nothing else breaks
With a kind of global setting or something? no.
I meant with some patch
EDIT: if this can't be fixed then I'm pretty sure that at least this change of the behaviour should be added to the migration guide
Issue seems to be fixed in latest Vue 2 (probably https://github.com/vuejs/vue/commit/f0057b101e6451d5095cdb7fd6308fd31ac0450c / https://github.com/vuejs/vue/issues/12703) and Vue 3 (probably https://github.com/vuejs/core/commit/78c199d6dbe8931520b75d8bfe0d49366a06922a / https://github.com/vuejs/core/issues/2291)