I also have a recomposition-related question: all of the <samples> and basically every code i know, ...
c
I also have a recomposition-related question: all of the samples and basically every code i know, uses the
kotlin.collections.List
interface to represent a list of data. However the
List
interface is not
@Stable
. My experience shows that if a composable function is called with the same
List
of strings, it will always recompose. Should we use another type to represent `List`s (and other collections) in compose? This could be also a broader question, since lots of built-in immutable types (like
LocalDateTime
) are not considered stable by the compose compiler. I added example code in đź§µ
⬆️ 1
đź‘€ 4
Copy code
@Composable
fun RecompositionTest() {
    var counter by remember { mutableStateOf(0) }
    val list = listOf("compose") // does not change

    LaunchedEffect(Unit) {
        while (true) {
            delay(1000L)
            counter++
        }
    }

    Content(list, counter)
}

@Composable
private fun Content(list: List<String>, counter: Int) {
    Text(text = "count $counter")

    MyList(list = list) // this will always recompose because List is not @Stable
    MyList(listWrapper = ListWrapper(list)) // only called once, ListWrapper is @Stable
}

@Composable
private fun MyList(list: List<String>) {
    Column {
        list.forEach {
            Text(text = it)
        }
    }
}

@Composable
private fun MyList(listWrapper: ListWrapper) {
    Column {
        listWrapper.list.forEach {
            Text(text = it)
        }
    }
}

@Stable
data class ListWrapper(val list: List<String>)
@Zach Klippenstein (he/him) [MOD] sorry for the ping, maybe you can add some info here?
By the way, i see there is a longer, similar thread here: https://kotlinlang.slack.com/archives/CJLTWPH7S/p1631639793248900
z
Based on that thread, I think the reason for this is probably partly to avoid doing potentially expensive whole-list comparisons on every recomposition, and probably also because
List
doesn’t guarantee immutability so stability can’t be inferred. One way to do this that doesn’t involve a wrapper, and is probably more performant, might be to use a
SnapshoteStateList
(i.e.
mutableStateListOf
). But if you really want to force a whole-list comparison every time and are confidant that the list will not be mutated then i guess the wrapper approach should work.
c
Thanks! Interesting. Comparing of (big) lists could be more expensive, then just to recompose them? Moreover, can you add more detail how should we use
mutableStateListOf()
? Just wrap every list what we have, and pass that down to composables?
z
I meant not wrapping lists, since that wouldn’t take real advantage of it. I meant using the mutable list under the hood
c
Just to be clear: all our lists are immutable underlying what we pass down to compose. What should we do in that case?
đź‘€ 2
z
Yes I realize that, what I was proposing is using mutable snapshot lists instead, although that might not work for your architecture so it's your judgement call. If you want to keep immutable lists, you could use a wrapper, or put your list into a State that is backed by a mutableStateOf with a structural equality mutation policy as soon as possible and pass the State around between composables
c
Thanks! I saw that there is a TODO to support
kotlinx.immutable
by default. I guess that would be the ideal solution. Until then, we could use a wrapper.
z
mutableStateListOf
is actually just basically a wrapper around an immutable list inside a mutable state.