Rulogarcillan
Rulogarcillan

Reputation: 1232

Why does Jetpack Compose trigger recomposition inefficiently with similar branches in a when statement?

I'm encountering an issue with Jetpack Compose where recomposition seems inefficient when using a when statement, even though the resulting composable is the same. Here are the simplified examples:

Example 1: Triggers Recomposition

when (theme) {
    ThemeOption1 -> MyComposable(content = content)
    ThemeOption2 -> MyComposable(content = content)
}

In this example, switching between ThemeOption1 and ThemeOption2 causes recomposition, even though the same composable MyComposable is being used in both branches.

Example 2: Does Not Trigger Recomposition

when (theme) {
    ThemeOption1,
    ThemeOption2 -> MyComposable(content = content)
}

In this example, grouping the when conditions to lead to the same composable prevents unnecessary recompositions.

Question: Why does Jetpack Compose not recognize that both branches of the when statement in the first example lead to the same composable and thus trigger recomposition? Is there a way to make Compose smarter in this regard or is this a limitation by design?

Additional Context: The theme variable is a state observed by the composable. The content of MyComposable does not change between different theme options. Any insights or recommendations to optimize this scenario in Jetpack Compose would be greatly appreciated. Thank you!

Upvotes: 1

Views: 60

Answers (1)

tyg
tyg

Reputation: 15579

That's by design and it is a necessity to ensure correctness, not an arbitrary limitiation.

Although both MyComposable(content = content) from the first example look the same they actually aren't: They are two separate composables in the compose tree. This becomes relevant if you take into account that each composable can have an internal state represented by what they remembered on previous recompositions.

Let's assume MyComposable looks like this (for brevities sake I ignored the content):

@Composable
fun MyComposable(content: @Composable () -> Unit) {
    var counter by remember { mutableStateOf(0) }
    counter++

    Text("$counter")
}

With this your Example 1 and Example 2 will produce a different output. Whenever MyComposable is called counter is incremented: In the second example this will be done for either ThemeOption1 or ThemeOption2, in the first example, however, you have two different counters, one that counts for ThemeOption1, the other that counts ThemeOption2.

So doing the additional recomposition that you found redundant actually is essential. It wouldn't be possible to accurately update the counters otherwise.

Upvotes: 2

Related Questions