When a State is read it triggers recomposition in nearest scope.
First example:
var timer by remember { mutableStateOf(0) }
SideEffect {
Log.i("info", "xxxx")
}
LogCompositions("JetpackCompose.app", "Composable scope")
LazyColumn {
item(contentType = "timer") {
LogCompositions("JetpackCompose.app", "Item Text Scope")
Text(text = timer.toString())
}
item {
LogCompositions("JetpackCompose.app", "Item Button Scope")
Button(onClick = { timer++ }) {
LogCompositions("JetpackCompose.app", "Button Scope")
Text(text = "Add")
}
}
}
When the screen is displayed you can find:
Compositions: Composable scope 0
Compositions: Item Text Scope 0
Compositions: Item Button Scope 0
Compositions: Button Scope 0
I xxxx
When the Button
"Add" is clicked only the item with the Text is recomposed.
Compositions: Item Text Scope 1
It happens because it recompose the scope that are reading the values that changes: Text(text = timer.toString())
.
Using a Column
instead of a LazyColumn
:
var timer by remember { mutableStateOf(0) }
SideEffect {
Log.i("info", "xxxx")
}
LogCompositions("JetpackCompose.app", "Composable scope")
Column {
LogCompositions("JetpackCompose.app", "Column Scope")
Text(text = timer.toString())
Button(onClick = { timer++ }) {
LogCompositions("JetpackCompose.app", "Button Scope")
Text(text = "Add")
}
}
When the screen is diplayed:
Compositions: Composable scope 0
Compositions: Column Scope 0
Compositions: Button Scope 0
I xxxx
When the Button
"Add" is clicked all the composable is recomposed:
Compositions: Composable scope 1
Compositions: Column Scope 1
I xxxx
Also in this case it happens because it recompose the scope that are reading the values that changes: Text(text = timer.toString())
.
The scope is a function that is not marked with inline and returns Unit.
The Column
is an inline function and it means that Column doesn't have an own recompose scopes.
To log composition use this composable:
class Ref(var value: Int)
// Note the inline function below which ensures that this function is essentially
// copied at the call site to ensure that its logging only recompositions from the
// original call site.
@Composable
inline fun LogCompositions(tag: String, msg: String) {
if (BuildConfig.DEBUG) {
val ref = remember { Ref(0) }
SideEffect { ref.value++ }
Log.d(tag, "Compositions: $msg ${ref.value}")
}
}