Lev Leontev
Lev Leontev

Reputation: 2615

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:

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

Upvotes: 5

Views: 1206

Answers (2)

Thracian
Thracian

Reputation: 67443

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.

Upvotes: 3

Steyrix
Steyrix

Reputation: 3266

It is possible. Unlikely to happen, but possible. In practice, fast click or double click problem is present in compose. In this particular case this can happen because the example composable is holding some kind of state.

There is a practice of making composable stateless and keeping states outside. Compose will be able to detect parameter changes outside particular recomposition flow and possibly cancel it. (In your case, it won't hide the button and therefore you won't reach the unexpected behavior)

From official documentation

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.

Upvotes: 1

Related Questions