Open stephen-mojo opened 2 years ago
If the model is rebound, wouldn't the click listener also be bound again with a closure on the most-up-date data anyways?
No, what you might be missing is that a re-bind for updated properties is incremental. It only rebinds the things that changed, and with DoNotHash applied the click listener will not be rebound. Take a look at the generated model code to see the implementation details. it would be possible to change this, and always rebind listeners in the incremental bind case, but it adds one more layer of complexity and special handling. maybe it's a great idea we just never considered though
another reason to use the onModelClickListener is that you can share the listener between all items instead of allocating a new one for each item in the list. this can make rebuilding models more efficient.
yes, it seems the docs got out of date in terms of animals and listings, feel free to send a PR to fix it!
Thank you for the response, but I am still not sure I completely follow.
No, what you might be missing is that a re-bind for updated properties is incremental. It only rebinds the things that changed, and with DoNotHash applied the click listener will not be rebound. Take a look at the generated model code to see the implementation details.
Are you saying that each individual attribute is inspected for a change before re-binding to a view at an attribute by attribute level?
It appears to me that EpoxyModel.bind()
is all or nothing. Either it binds all the model's attributes (in the case that it is called) or none of them (in the case that it is not called). Also EpoxyModel
setter methods seem to always overwrite attributes even when DoNotHash
is set on them. This leads me to believe that if any individual EpoxyAttribute
changes, then the overall model hash changes, and then all attributes are bound to the view in EpoxyModel.bind()
, even those that are marked DoNotHash
.
For example, the generated code for setting an DoNotHash
onClickListener
is like this. Is not the click listener being overwritten each time?
public ListItemEpoxyModel_ onClickListener(final OnModelClickListener<ListItemEpoxyModel_, ListItemEpoxyModel.Holder> onClickListener) {
onMutation();
if (onClickListener == null) {
super.onClickListener = null;
}
else {
super.onClickListener = new WrappedEpoxyModelClickListener(onClickListener);
}
return this;
}
public ListItemEpoxyModel_ onClickListener(View.OnClickListener onClickListener) {
onMutation();
super.onClickListener = onClickListener;
return this;
}
Where in the code is this incremental diffing done?
I am a bit confused by the documentation of
DoNotHash
andOnModelClickListener
.I would like to start by saying the documentation goes from talking about
Animals
to talking aboutListings
:I still think I get what is being communicated and feel that the problem
OnModelClickListener
it is solving makes sense to me, but how it solves it does not.If we are to store all the data we care about in the model to be retrieved later in an
OnModelClickListener
callback, what is the point of theOnModelClickListener
? If the important data is now part of the model as an Epoxy attribute, wouldn't it result in a rebind with a new closure anyways?Explanation:
It is my understanding that using
DoNotHash
on a click listener Epoxy attribute in an Epoxy model can result in an a stale listener with a closure over an out-of-date object. This can happen when none of the Epoxy attributes on your model change, but other attributes of your source data do. Because nothing marked as an Epoxy attribute has changed, Epoxy doesn't bind a new click listener. The old click listener is still in place with a closure over an old object.Example: Say our source data is made up of a list of Animal Kotlin data classes:
The Epoxy model for an animal has the following attributes and only visually shows the name:
In your Epoxy controller, you loop over a list of animals and make a model for each one:
Now say the age of a few animals change and you now have a new list of animals. When the controller loops over the animals, no Epoxy attributes change so nothing is rebound. When the user clicks on an animal, the wrong age is printed due to a stale closure.
What I do not understand is how
OnModelClickListener
solves this.It is my understanding that
OnModelClickListener
always gives you the most up-to-date model even if the view was never re-bound.The documentation states:
If we follow this advice and store all data that we care about on the model as an Epoxy attribute, wouldn't that mean if said data changed, the hash code of the model data would change thus triggering a new bind? If the model is rebound, wouldn't the click listener also be bound again with a closure on the most-up-date data anyways?
Example: Say we add all the data we care about to the model (even stuff we don't visually show):
It seems to me, both of these work, but the
OnModelClickListener
isn't necessary.Option #1: Read from closure
Option #2: Read from model
Or alternatively, we store the entire Animal object in the model:
Option #1: Read from closure
Option #2: Read from model