Home > Enterprise >  Unwanted recomposition when using Context/Toast in event - Jetpack Compose
Unwanted recomposition when using Context/Toast in event - Jetpack Compose

Time:12-03

In a Jetpack Compose application, I have two composables similar to here:

@Composable
fun Main() {
    println("Composed Main")
    val context = LocalContext.current

    var text by remember { mutableStateOf("") }

    fun update(num: Number) {
        text = num.toString()
        Toast.makeText(context, "Toast", Toast.LENGTH_SHORT).show()
    }

    Column {
        Text(text)
        Keypad { update(it) }
    }
}

@Composable
fun Keypad(onClick: (Number) -> Unit) {
    println("Composed Keypad")

    Column {
        for (i in 1..10) {
            Button(onClick = {onClick(i)}) {
                Text(i.toString())
            }
        }
    }
}

Clicking each button causes the two composables to recompose and produces this output:

I/System.out: Composed Main
I/System.out: Composed Keypad

Recomposing the Keypad composable is unneeded and makes the app freeze (for several seconds in a bigger project).

Removing usages of context in the event handles (in here, commenting out the Toast) solves the problem and does not recompose the Keypad and produces this output:

I/System.out: Composed Main

Is there any other way I could use context in an event without causing unneeded recompositions?

CodePudding user response:

That's actually weird why Toast affects the recomposition of KeyPad when its showing, but considering Toast is kind of a side-effect not an actual part of the composable (I'm expecting a correction here) I thought of putting it in an Effect to prevent Keypad from recomposing, and it did.

Here, SideEffect will execute every post-recomposition.

SideEffect {
   if (text.isNotEmpty()) {
       Toast.makeText(context, "Toast", Toast.LENGTH_SHORT).show()
   }
}

or you can utilize LaunchedEffect using the text as its key, so on succeeding re-compositions, when the text changes, different from its previous value (invalidates), the LaunchedEffect will re-execute and show the toast again

LaunchedEffect(key1 = "text") {
   if (text.isNotEmpty()) {
       Toast.makeText(context, "Toast", Toast.LENGTH_SHORT).show()
   }
}

Replacing your print with Log statements, this is the output when clicking any buttons, either of the effect being used

E/Composable: Composed Main   // first launch of screen
E/Composable: Composed Keypad // first launch of screen

// succeeding clicks

E/Composable: Composed Main
E/Composable: Composed Main
E/Composable: Composed Main
E/Composable: Composed Main
  • Related