Anthone
Anthone

Reputation: 2290

Android compose recomposition experience

I'm exploring Compose Recomposition with an Experience composable, and I'm a bit confused about its behavior.

In the first code snippet, Experience() was not recomposed after the button click:

@Composable
fun Experience() {
    println("Expericence() : Recomposition")

    var j = 5
    var i by remember { mutableIntStateOf(5) }

    Row {
        Button(
            onClick = {
                i++
                j++
                var text  = "i=$i j=$j"
                println(text)
            }
        ) {
            var text  = "i=$i j=$j"
            println("Button() : Recomposition")
            Text(text)
        }
    }
}

However, in this second snippet, Experience() is recomposed:

@Composable
fun Experience() {
    println("Expericence() : Recomposition")

    var j = 5
    var i by remember { mutableIntStateOf(5) }
    

    Row {
        var text  = "i=$i j=$j"
        Button(
            onClick = {
                i++
                j++
                println(text)
            }
        ) {
            println("Button() : Recomposition")
            Text(text)
        }
    }
}

Can someone explain why this is happening? In my understanding, in the second code snippet, only Row and its children should be recomposed, not the entire Experience() composable.

Upvotes: 1

Views: 317

Answers (2)

SnuKies
SnuKies

Reputation: 1723

I suggest reading the article What is “donut-hole skipping” in Jetpack Compose?. It's exactly your usecase

To answer your question, the recomposition happens because you read the i variable inside Row {} in your second snippet.

Compose will recompose to the nearest scope. Normally, that'd be Row, however, Row is inline, so the nearest scope is your Experience composable

Upvotes: 3

Skizo-ozᴉʞS ツ
Skizo-ozᴉʞS ツ

Reputation: 20646

Recomposition happens when any part of a Composable function changes, then the entire composable is recomposed.

First code snipped

Even if you extracted the Text into a variable, the reference to the text is still within the scope of the Button's lambda, and then it changes.

Second code snipped

Even if you moved the Text variable outside the Button's lambda it is not a part of the lambda's scope, now, changes to i and j will only trigger recomposition of the Button and its childrens, not the entire Experience composable.

If you want to avoid recomposition you may use derivedStateOf to create a derived state that only depends on the specific variables you want.

val buttonText by remember(i, j) {
        derivedStateOf {
            "i=$i j=$j"
        }
    }

Upvotes: 0

Related Questions