Is it possible to customise the equality predicate...
# compose
t
Is it possible to customise the equality predicate used by Compose to determine whether recomposition is required (i.e. observe particular fields of an object)?
z
It is for `MutableState`s,
mutableStateOf
takes a SnapshotMutationPolicy which lets you define both equality and merge behavior
Although this doesn’t just apply to recomposition, it applies to any snapshot state observers (including e.g.
snapshotFlow
)
t
I'm using the
collectAsState()
operator on a
SharedFlow
, with objects whose equality is determined by their ID. The SharedFlow emits, but the layout doesn't recompose
I could just change the equality function to involve those fields, but I'm curious whether there's another approach. I'm guessing
collectasState
uses
mutableStateOf
under the hood..
Obviously I don't understand how this stuff works under the hood. But it looks like I might be able to create my own version of
collectAsState
which accepts a
SnapshotMutationPolicy
as an argument. I'll give that a go
Hmm, it might be the
LaunchedEffect
in
produceState()
which is making the comparison in this case.
z
Have you looked at the implementation of
collectAsState
? Might answer your question easier than posting here 😉
I take it yes from that last comment lol
You could certainly make your own version of
collectAsState
which uses a custom snapshot mutation policy, but the first question I would ask is why?
You shouldn’t rely on how often something does or doesn’t recompose for correctness.
I think the only time to really care about it is if you’ve done your own benchmarking and can prove that over-recomposition is actually causing performance problems
t
My problem is I've changed the equality predicate of a model to be based on ID (rather than comparing all fields). But, I would like recomposition to occur when one of those fields changes.
z
Good luck – i think you might run into all kinds of other friction with that approach, most of the compose stuff around equality (and most code in general) assumes that
.equals
will check all the content afaik (e.g. function argument comparisons). If you can make it work, good for you, but i would definitely consider flipping that around and making the places that need to compare by ID only explicitly do so, and make
equals
do the standard thing.
2
t
I agree with you that this is the path of least resistance (and I have flipped things around for now). I don't agree that most code in general assumes
.equals
will compare all fields. This is the default for a Kotlin data class, sure. Maybe it's a little too philosophical, and maybe my usual approach is flawed - but usually if I'm dealing with an object coming back from a database, I check equality based on the ID. If I need to know whether contents have changed (for example, RecyclerView DiffUtil), then I will add an additional 'contents comparison' check. The assumption made by compose that
.equals
checks all content is exactly the behaviour I'd like to override though. I think it's a little backwards to be designing my objects for their eventual composition/recomposition. But, I'm new to the compose way of thinking and I accept that I could be completely wrong about all of this!