Compose noob here, i'm trying to use `keys` to avo...
# compose
s
Compose noob here, i'm trying to use
keys
to avoid recomposition of previously composed ones but can't seem to get it to work. Displaying a list with items and a button to shuffle the list item but all items are composed again (logging via
SideEffect
)
Copy code
data class Movie(val id: String, val title: String)

@Composable
fun MovieList() {
    var movies by rememberSaveable {
        mutableStateOf(List(100) {
            Movie("$it", "Movie $it")
        })
    }

    Column {
        Button(
            onClick = { movies = movies.shuffled() }
        ) {
            Text(text = "Shuffle Movies")
        }
        LazyColumn {
            items(movies) { movie ->
                key(movie.id) {
                    MovieListItem(movie)
                }
            }
        }
    }
}

@Composable
fun MovieListItem(movie: Movie) {
    Text(text = movie.title)
    SideEffect {
        Log.e(TAG, "MovieListItem composed for $movie")
    }
}
s
Ah, thought i saw some reference to key composable, tried with key parameter too but still seeing the same behavior
Copy code
LazyColumn {
            items(movies, key = { it.id } ) { movie ->
                MovieListItem(movie)
            }
        }
z
Why are you trying to avoid recomposition? That’s not really
key
or the key parameter is for anyway.
s
Oh just trying to assert a suggestion came across in the docs
https://developer.android.com/jetpack/compose/lifecycle
Copy code
Key Point: Use the key composable to help Compose identify composable instances in Composition. It's important when multiple composables are called from the same call site and contain side-effects or internal state.
z
Yea that’s not about avoiding recomposition, it’s about correctly identifying group of composables as the order of items in the list changes.
s
Ah interesting, so what exactly do they mean by reusing
instances haven't changed, and can reuse them
. What is being reused?
Looks like it is referring to composable instances being reused. Getting head wrapped around functions and instances 🙂
z
It means any state in those composables, or any effects they’re running, will continue to exist and not be removed from the composition or disposed
In other words, without the key, compose would just thinking the blue box “became” the orange one, and the light green one became the light blue one, and the light green one on the right was new. They key gives compose the knowledge that each of those has some identity, and so it knows that the blue one is the same box across both recompositions.
s
Gotcha, this would help for any long running effects while new items might pop in the list.
Also, why couldn't compose have just used
equals
by default to identify them than having devs provide a key?
I guess, could be related to performance given many possible states etc.
z
That would almost always be the wrong thing, since lazy lists are usually made from lists of e.g. data classes, where there’s a single proeprty representing ID but the rest of the fields don’t define the list item’s “identity”
e.g. if the number likes on a tweet changes it’s still the same tweet
s
Sure, but as long as they also check for primary ids, should still bail out. But yes, can see some data set without those identifiers.
z
Who’s “they”, and how would they know what a primary ID is without being told? That’s exactly what keys are for.
☝️ 2
s
I had meant, compose internals and a brute force equals of all fields like
id
&
title
in this case
😱 1
Or use
hashCode
of the state and use that as an identifier. But this can work only if there is a unique identifier within the data set
z
that would be entirely too much magic for a UI framework to be doing, trying to infer the high-level semantics of everyone’s domain models 😅 And hashcode often takes all fields of a class into account, which is the same problem re: twitter likes vs identity.
s
Yeah for sure. Anyways, after trial and error can appreciate the cool optimization, esp for dynamic lists.