Reputation: 53
I'm probably missing some important composable recomposition concepts here but let me explain my doubt.
I was reading about side effects in Android Compose and I findout that specifically SideEffect composable function gets executed only when the parent composable function is recomposed. Meaning, every time parent composable function is recomposed, any effect of a SideEffect will be executed. Now, recomposition happens when:
So if we have this simple counter composable function called Counter1, SideEffect inside of it will be executed when this function first launches and, after that, every time the user increases the count (every time the user clicks on the Button):
@Composable
fun Counter1() {
var counter: Int by remember { mutableStateOf(0) }
SideEffect {
Log.d("Test tag", "Counter1: $counter")
}
Column {
Button(onClick = { counter++ }) {
Text(text = "Increase count")
}
Text(text = "Counter value is: $counter")
}
}
If we change that counter a little bit and we produce this other one:
@Composable
fun Counter2() {
var counter: Int by remember { mutableStateOf(0) }
SideEffect {
Log.d("Test tag", "Counter2: $counter")
}
Column {
Button(onClick = { counter++ }) {
Text(text = "Increase count is: $counter")
}
}
}
SideEffect in this second scneario will only be executed the first time the composable is recomposed. It will not be executed when the user changes the state (by increasing the counter). Why is this happening? I put a break point at the first line in Counter2 and every time the user clicks on the button, the counter value is increased and the Counter2 is reinvoked with a new state.
Upvotes: 3
Views: 678
Reputation: 14984
To expand on @Thracian's comment, what SideEffect
interprets as a recomposition is restricted to the part of the compose tree where SideEffect
is located. Yes, there is a recomposition everytime the counter is increased, but not everything is recomposed, just the affected composables.
Although the counter
state variable in Counter2 is in the same compose scope as the side effect, it is not used anywhere in that scope so no recomposition is necessary (and the side effect is skipped). It is used in the Button
composable to call Text
, but that is a separate compose function that has its own scope.
The interesting part is why Counter1 behaves differently. Now the counter
is used only in the Column
composable to call Text
, but Column
also is another composable function that should have its own scope and should therefore behave the same (which it doesn't).
Here is where @Thracian's comment comes into play: Column
is not just a compose function, it is also declared as an inline
function. That means that the compiler inlines the function by replacing every call of the function with a copy of its body, then the function is removed. In the compiled code there is no call to another compose function, therefore no new compose scope is created and the content of Column
uses the surrounding scope instead. And that happens to be the same where the SideEffect
is located.
That is why changing counter
in Counter1 triggers a recomposition of the scope that contains the SideEffect
, because it is the same that contains the Text
(that accesses counter
).
Upvotes: 2