How to reset a reused component's state?


#1

Ember seems to reuse component instances across route transitions. The problem is that view state (i.e. properties on my component) doesn’t get reset. Where should I reset the state? The only solution that came to my mind is implementing a service that notifies the component of a route transition. But this seems quite ugly.


#2

The problem is that view state (i.e. properties on my component) doesn’t get reset.

Your component state depends on properties being passed to it. If your properties change, component state changes as well.

Where should I reset the state?

If properties passed to component are on Controller, see resetController hook in Route. http://emberjs.com/api/classes/Ember.Route.html#method_resetController

You can use that hook to (re)set some properties on route exit.

I hope it answers your question.


#3

I’m talking about internal, non-persistant properties. These remain the same. That’s also why I can’t use controller logic. But analogues to the resetController hook, I was expecting a reset hook for components.

Can I use any of the hooks mentioned in this change log? Curiously I wasn’t able to find most of them in the official API docs.


#4

I’m talking about the internal, non-persistant properties.

What do you mean by this?


#5

I mean properties that aren’t saved to or loaded from the model and hence are volatile. For example sort options. I want to reset them within my component when a route transition happens.


#6

Components aren’t singletons, can you post the code from the component for us to review?


#7

I’m aware of that. Still as far as I can tell they get reused in some situations (probably for optimization).

Here is a twiddle.

Note how after selecting a route and clicking the button the clicked property remains true across route transitions.

I’m looking for a component hook to reset this clicked property. I would think that this is a common use case that doesn’t need a workaround.


#8

@ctusch We typically use the didInitAttrs and didUpdateAttrs hook for this kind of work. Details about that can be found here: http://emberjs.com/blog/2015/06/12/ember-1-13-0-released.html#toc_component-lifecycle-hooks

I also accidently just stumbled across this ember-twiddle that might be handy for you to poke at: https://ember-twiddle.com/2f9d698cdaed87821623 (obvious you have to open the dev console to the log statements).


#9

Hmmm, that is odd behaviour, I don’t know if that would qualify as a bug but it isn’t what you’d “expect”. As @workmanw says, you can use didUpdateAttrs to fix this pretty easily but it’s still a bit of a quirk.


#10

I’m pretty sure that it is not a bug. That is just part of how Ember/Glimmer works. All that’s really changing is the data bindings. The route/view is the same, you’re just changing models. I believe it has worked this way for a long time (maybe since 1.0).


#11

Have you tried resetting internal props in didInsertElement() or even init()?


#12

@smashweaver These are not called again when the component is reused.

@workmanw Thanks! I wasn’t sure if this was the right hook but I just found it officially mentioned in the guides!


#13

They really should think about letting people know about this in big bold letters. I had your exact same issue on an internal project of ours. I figured it was a bug and just worked around it thinking it would get fixed in the future…


#14

Agreed. I had a similar issue – component state seemed to be leaking and I realized that the same DOM node was being reused when I assumed it would have changed. Because the compare/contrast was made that “controllers are singletons but components are instances”, I assumed that it wouldn’t reuse the same component when I changed the data behind it. Makes sense now, but could be good to have mentioned in the docs.


#15

I use didReceiveAttr to handle this, but it is not perfect b/c the Attr content might change but the Attr didn’t change, in which case a re-render will not happen. I will have to manually add all sensitivity list to the attr of the component and check all of them in didReceiveAttr. Another problem is didReceiveAttr could be fired multiple times for attrs that we don’t care.