Search code examples
androidkotlinandroid-jetpack-composerace-conditioncompose-recomposition

Jetpack Compose recomposition race condition


Suppose we have the following code. It displays a button, and when the user clicks on it, the button disappears.

@Composable
fun ButtonThatDisappearsOnClick() {
    var showButton by remember { mutableStateOf(true) }
    if (showButton) {
        Button(onClick = {
            check(showButton) { "onClick shouldn't be called for a hidden button" }  // !!!
            showButton = false
        }) {
            Text("My button")
        }
    }
}

I suspect that the check call above may fail if the user clicks the button twice really quickly:

  • The user clicks the button, shouldShowButton is set to false. Since the value in a mutable state was updated, a recomposition is scheduled.
  • The user clicks the button very quickly again before the views have been recomposed. Thus, the onClick function will fire the second time, and the check call will fail.

I have not been able to reproduce this in practice, so I am wondering if such a behavior is indeed possible.


Solution

  • This question is answer what you ask for. Op changes values consecutively but if state value changes before recomposition is completed it's a possibility that previous recomposition is discarded and new one is scheduled.

    MutableState ignores first value if set two values in a row

    SideEffect function can be used for operations that should be invoked only when a successful recomposition happens

    Recomposition starts whenever Compose thinks that the parameters of a composable might have changed. Recomposition is optimistic, which means Compose expects to finish recomposition before the parameters change again. If a parameter does change before recomposition finishes, Compose might cancel the recomposition and restart it with the new parameter.

    When recomposition is canceled, Compose discards the UI tree from the recomposition. If you have any side-effects that depend on the UI being displayed, the side-effect will be applied even if composition is canceled. This can lead to inconsistent app state.

    Ensure that all composable functions and lambdas are idempotent and side-effect free to handle optimistic recomposition.