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

watcher for injected value is triggered before destroying the component displayed with v-if="value" after the value is set to falsy #6389

Closed
jacekkarczmarczyk opened this issue Jul 31, 2022 · 5 comments

Comments

@jacekkarczmarczyk
Copy link
Contributor

jacekkarczmarczyk commented Jul 31, 2022

Vue version

3.2.37 and 2.7.8

Link to minimal reproduction

https://sfc.vuejs.org/#eNqFUstugzAQ/JWVLxAJcNojJZWqfkJ79AXI0hAF2/KDHJD/vWtAKE2k9ObdnR17ZjyxD62L0SMrWWVb02sHFp3X70L2g1bGwQTaqLE/YgYGuwxOEKAzaoCE1pK3Dfflm09FR4nSrYCC3zbjNREPIGSrpCWUUnCIrOlu7sfJelma0DDJIiTO6E3f/YDKuzTdweEdUhoUY33xSAwTNLUpwRmqwi6Dl/1+//+Wl0fseolH2nhdNyq+mEDyqXA46EvtMFausr7J203hmPfdQTAiFAw4ISq+wVnGFlPyodbF2SpJ9k6z8nVgBSth7sQeGRNrwU7OaVtybrs2unW2hTI/nE6F8dKRlALtkDdGXS0aIhYsu+Hg1BzR5AZJmUHzjPMO+sAbaYOQgaTcZ/jsq/TyjK3L4Fq79uGj/I19gS45R+uFnJfWpLagCoo2Axt7U3gS0TTRSgi3KYRfpgb7pw==

Steps to reproduce

  1. provide a ref value with object/undefined
  2. display subcomponent conditionally with (v-if="value")
  3. watch some property of the injected value in the subcomponent
  4. 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)

@jacekkarczmarczyk jacekkarczmarczyk changed the title watch is being executed after the component is (or is supposed to be) destroyed watcher for injected value is triggered before destroying the component displayed with v-if="value" after the value is set to falsy Jul 31, 2022
@lidlanca
Copy link
Contributor

in case this is not bug , you can use the watch flush:post option

playground - flush:post

@jacekkarczmarczyk
Copy link
Contributor Author

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

@LinusBorg
Copy link
Member

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.

@jacekkarczmarczyk
Copy link
Contributor Author

jacekkarczmarczyk commented Aug 1, 2022

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

@jacekkarczmarczyk
Copy link
Contributor Author

Issue seems to be fixed in latest Vue 2 (probably vuejs/vue@f0057b1 / vuejs/vue#12703) and Vue 3 (probably 78c199d / #2291)

@github-actions github-actions bot locked and limited conversation to collaborators Sep 22, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants