Hi everybody - I came across a really strange reco...
# compose
d
Hi everybody - I came across a really strange recomposition issue that I'd like to better understand. We have a minimal state class containing a simple integer and boolean
Copy code
data class ScreenState(val intValue: Int = 0, val boolValue: Boolean = false)
We have a screen composable that uses a composable based on the state. For the example we increment the integer value within the state once per second. Note that the boolean isn't modified.
Copy code
@Composable
fun TestScreen() {
    // Screen state managed by the composable
    val screenState = remember { mutableStateOf(ScreenState()) }
    /**
    Update the integer value of the state once per second
    Note that the boolean value is never changed and is always false
     **/
    LaunchedEffect(key1 = null) {
        while (true) {
            delay(1000)
            screenState.value = screenState.value.copy(
                intValue = screenState.value.intValue + 1,
            )
        }
    }
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color(0xFF272727)),
        contentAlignment = Alignment.Center
    ) {
        // Simple composable that takes the state object as an input
        ContainerBox(screenState.value)
    }
}
The ContainterBox is an outlined column that takes the state in and uses the boolean to selectively include a composable using the integer value
Copy code
@Composable
fun ContainerBox(value: ScreenState) {
    Column(
        modifier = Modifier
            .width(300.dp)
            .height(300.dp)
            .border(
                border = BorderStroke(1.dp, Color(0xFF23C42A)),
                shape = RoundedCornerShape(10.dp)
            )
            .clip(RoundedCornerShape(10.dp))
            .background(Color(0xFF75757A)),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        if (value.boolValue) {
            Text("Dynamic Text ${value.intValue}")
        } else {
            Text("Static Text")
        }
    }
}
The expectation is that since boolValue isn't being modified that ContainerBox wouldn't need to recompose. However, we see a recomposition every second. After quite a bit of debugging, we found the border modifier to be the culprit. If we take the border modifier out, the recomposition isn't performed until boolValue is changed. Does anybody know what about the border modifier is triggering the unneeded recompositions?
đź‘€ 3
p
I believe the level of smartness of the compose compiler is not at that level yet. You are telling the compiler
ContainerBox(value: ScreenState)
ContainerBox depends on a State and it is updating your composable with the new State value. As far as I know I haven’t read that level of precision, where compose can go even further and check what properties inside the State are actually needed in a specific Composable function.
d
I thought the same thing initially, but it seems to be fairly good from what I can tell. If the border modifier is removed from that inner Column, there aren't any unexpected recompositions
p
Humm, interesting. It is really smart in that sense then.
I have experience that too with some Composables where adding certain Modifiers/animation trigger recomposition. Perhaps is not related to your State changing but some internal State that Composable use
z
There is a really weird bug with the border modifier that I think results in extra recompositions. I’m not sure if that’s what you’re hitting here but sounds suspicious
d
It sure sounds like it could be related. Is there something logged against it? In our specific application it doesn't hurt too much, but I could see it being a problem for something more complex