I need help in solving many-recompositions puzzle. Reading a `LazyList` state causes the endless rec...
d
I need help in solving many-recompositions puzzle. Reading a
LazyList
state causes the endless recomposition loop, I can't fully understand why. Minimal sample is in the thread.
Copy code
@Composable
fun Component() {
  val content = (1..80).map { "Item $it" }
  val state = rememberLazyListState()
  val value = (state.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0) // !!!
  println("recomposing")
  LazyColumn(modifier = Modifier.fillMaxSize(), state = state) {
    items(content) {
      Text(text = it)
    }
  }
}
The code above causes an endless stream of "recomposing" in the logcat. But if I comment out the line marked with
!!!
only one "recomposing" is printed and there's no endless loop. What happens here?
d
Don't you recreate a new
content
at each composition?
d
in this sample yes. but in my app I don't do this. And still why removing
!!!
line stops this endless loop?
d
You need to wrap value with remember and derived state of.
d
I suspected something like this but I don't fully understand the mecahnics here...
d
Can't be arsed to explain on my phone but if you search slack you'll find something.
d
Haha, OK. But this did help, thanks.
I guess I'll also have to re-read Zach's article on snapshots. I thought that only writing values causes recomposition, but it seems that reading does this too sometimes
d
Kinda. Writing does but if the value was read.
z
Yea, LazyColumn writes to the states in the lazy list state, if it didn’t it wouldn’t be very useful.
I am surprised that this doesn’t eventually settle though. State changes shouldn’t trigger change notifications if the value didn’t actually change, and I would expect two compositions only: one before the list had a chance to do its initial layout pass, and then another after that initial pass once the state had been “initialized”. Is
value
actually different each time?
d
no, each time it's the same. and I don't scroll or interact with the list at all. and still have this endless recompositions.
d
It's a common issue with lazy column. Not sure why.
z
Sounds like something is using the wrong mutation policy in that state.
a
Yes, state.layoutInfo is updated after every measuring of LazyColumn. there is no mutation policy guarding it in order to not perform equals checks on list of items. you should use derivedState for such kind of observation, like described here: https://developer.android.com/jetpack/compose/lists#react-to-scroll-position
👍🏻 1
Basically what happens is during the recomposition you produce a new content lambda, a new content lambda results in a new measure, new measure results in a new layoutInfo, it results in a new recomposition and so on
d
Yes,
derivedStateOf
helped to fix this. Thank you for explanation!