Vue: Events in `updated` not triggering dom or v-model update

Created on 24 Jan 2017  Â·  6Comments  Â·  Source: vuejs/vue

Vue.js version

Most recent

Reproduction Link

https://jsfiddle.net/mo48q36L/

Each custom select element has a togglable option to represent an option that might change in real-world conditions. I want to prevent the value in vue and the value in the select element from being out of sync.

To do this, I compare the value of the select element and the value in vue in the updated lifecycle hook. If they're not equal, that means that the corresponding option no longer exists, so I want to simply set the value of both to be null.

Steps to reproduce

Click the "Toggle option" buttons below each custom select element, which will remove the currently selected element.

The top element (custom-select) does emit the null value, but the dom isn't updated until something else triggers a dom update. If vue devtools worked on jsfiddle, you would also be able to notice that the corresponding data value in the parent component is immediately updated after emitting null, but the value prop in the child component is not updated until the dom updates.

The bottom element (custom-select-next-tick) emits the null value in $nextTick, and the dom is updated in the next tick as it should be.

What is Expected?

I expect that emitting a new value in updated should trigger a new dom update.

The docs suggest that the dom will be updated by manipulating state in updated: https://vuejs.org/v2/api/#updated

What is actually happening?

Emitting a new value in updated is not triggering a dom update or v-model value change unless it's wrapped in $nextTick.

Thank you!

Most helpful comment

This works as intended. Changing data in updated is, sadly, very hairy behavior and implementation specific. I would recommend using $nextTick to hint Vue schedule or just updating dom manually.

The problem here is that Vue schedules data changes and watchers internally, and after every watcher has run, updated hook will get called. So your data change gets no chance to be captured or executed. When wrapped in $nextTick, the change is scheduled in next update queue.

The doc correctly warns against changing state, but the reason might be misleading.

@chrisvfritz Any idea?

All 6 comments

This works as intended. Changing data in updated is, sadly, very hairy behavior and implementation specific. I would recommend using $nextTick to hint Vue schedule or just updating dom manually.

The problem here is that Vue schedules data changes and watchers internally, and after every watcher has run, updated hook will get called. So your data change gets no chance to be captured or executed. When wrapped in $nextTick, the change is scheduled in next update queue.

The doc correctly warns against changing state, but the reason might be misleading.

@chrisvfritz Any idea?

@HerringtonDarkholme Unless I'm misunderstanding the use case, I don't think I'd even use $nextTick here - just computed properties or watchers. An advantage is the select will be updated even if no other DOM updates would have been made (e.g. if the togglable option were removed by a state change that was not initiated by the current user). I just updated the doc to make this clearer.

Sorry, computed properties or watchers for which part?

I want to make the validity check after any dom changes, so that's why I used updated. If I instead use a watcher for the value prop, will that take into account any dom changes that take place in the same tick?

I don't think I can use a computed property, because I want to be sure to emit the corrected/verified value back to the parent component as immediately as possible.

If I use a computed property for value instead, the component will still rerender after I emit the updated value, right? So in that case I don't see the advantage of using a computed property.

Please disregard my third paragraph in the above comment, it doesn't make any sense.

Edit: Oh, unless events emitted in the computed property function don't trigger the rerender either. But if that's the case then it seems like I'm duplicating state a little bit, I think I'd rather just eat a second render cycle.

I mean something like this. Notice I have the watcher in the parent component, rather than the custom input. Using the updated hook for this doesn't just trigger a second render cycle - it makes your application more brittle, since that parent component is no longer the sole source of truth.

For example, if you changed the parent component to be passed dynamic props instead of assigning initial data, there'd be a chance your select could _start_ with an invalid state. Also, the select would need to be rendered at all times for the value in the parent to be updated properly.

Oh I see. You're right, that is better. Thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

robertleeplummerjr picture robertleeplummerjr  Â·  3Comments

seemsindie picture seemsindie  Â·  3Comments

lmnsg picture lmnsg  Â·  3Comments

bdedardel picture bdedardel  Â·  3Comments

hiendv picture hiendv  Â·  3Comments