androidkotlinandroid-jetpack-composeside-effects

Executing a side-effect without an appropriate handler: What's are possible consequences?


Within a composable: I print a message to the terminal, using 'SideEffect'.

SideEffect {
    println("SideEffect -> ${System.currentTimeMillis()}")
}

The result is: SideEffect -> 1744447997722

When I write the 'println' in the composable without the SideEffect around it works just fine as well.

I know that "SideEffect" executes the code within the braces each time the re-composition has finished. But superficial considered it seems to make no difference, if I execute the println with or without SideEffect.

So what's the difference?

What might go wrong if I don't use SideEffect?


Solution

  • SideEffect is used in composable like Slider or TextField to update value only if a recomposition is successful. And also useful for logging recompositions, especially before before layout inspector was able to show recompositions.

    In logging where you don't read a State in println it might be needed if recomposition is always guaranteed to be triggered but in cases where you log changes in States might cause false positive recompositions. And because of that logging recompositions is done inside SideEffect.

    Consider example below where counter1 is read via println which triggers recomposition for entire

    SideEffectSample scope. In this composable there shouldn't be any recomposition, and in practical examples sometimes it's harder to see what's causing recompositions, since we are actually reading neither counter1 nor counter2 in that scope other than debugging purposes.

    For counter2, you can observe SideEffect block triggers only when counter1 changes or actual State in your composable which returns correct logging results.

    @Preview
    @Composable
    fun SideEffectSample() {
        Column {
            var counter1 by remember {
                mutableStateOf(0)
            }
    
            var counter2 by remember {
                mutableStateOf(0)
            }
    
            println("Counter1: $counter1")
    
            SideEffect {
                println("Counter2: $counter2")
            }
    
            Button(
                onClick = {
                    counter1++
                }
            ){
                Text("Counter1: $counter1")
            }
    
            Button(
                onClick = {
                    counter2++
                }
            ){
                Text("Counter1: $counter2")
            }
        }
    }