Is there any difference between the following two functions:
First function with derivedStateOf:
@Composable
private fun CounterWithDerivedState() {
val counterState = remember { mutableStateOf(0) }
val showFinallyState = remember {
derivedStateOf { counterState.value > 10 }
}
Button(
onClick = { counterState.value = counterState.value + 1 }
) {
Text(counterState.value.toString())
}
if (showFinallyState.value) Text("Finally!")
}
Second function with remember, key and MutableState:
@Composable
private fun CounterWithRemberMutablState() {
val counterState = remember { mutableStateOf(0) }
val showFinallyState by remember(counterState) {
mutableStateOf(counterState.value > 10)
}
Button(
onClick = { counterState.value = counterState.value + 1 }
) {
Text(counterState.value.toString())
}
if (showFinallyState) Text("Finally!")
}
I would expect the same behavior in terms of recomposition.
Edit:
As it turned out, the information provided at the sources (#1 #2) I was basing my answer upon were were not completely accurate. The answer below includes my latest findings made using the Layout Inspector.
Please check out the documentation of remember
:
Remember the value returned by calculation if
key1
compares equal (==
) to the value it had in the previous composition, otherwise produce and remember a new value by calling calculation.
If you use the declaration
val showFinallyState by remember(counterState) {
mutableStateOf(counterState.value > 10)
}
then the value of showFinallyState
will be recomputed each time the counterState
changes. However, when the output of the calculation actually has not changed from the previous value, the Composables depending on showFinallyState
will not recompose. That means that the first recomposition will happen after counterState
is > 10
.
Now let's check check the documentation of derivedStateOf
:
You should use the
derivedStateOf
function when your inputs to a Composable are changing more often than you need to recompose.
In your first code snippet
val showFinallyState = remember {
derivedStateOf { counterState.value > 10 }
}
you will only get a recomposition of all Composables depending on showFinallyState
once the counterState
actually is > 10
.
So actually, the two approaches result in the same amount of recompositions in dependent Composables.
I used the following Composable to investigate the cause:
@Composable
fun SampleComposable() {
val counterState = remember { mutableStateOf(0) }
val rememberKeyVariable = remember(counterState.value) {
mutableStateOf(counterState.value > 10)
}
val derivedStateVariable = remember {
derivedStateOf { counterState.value > 10 }
}
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Button(
onClick = { counterState.value = counterState.value + 1 }
) {
Text("INCREASE COUNTER")
}
Text(text = "rememberKeyVariable: ${rememberKeyVariable.value}")
Text(text = "derivedStateVariable: ${derivedStateVariable.value}")
}
}
This is the output of the Layout Inspector after clicking the Button eleven times:
In the Layout Inspector, we can see that both Text
Composables actually can skip ten recompositions, until finally the counterState
is > 10
, at which point they recompose.
Feel free to comment in case you find any flaws with the setup of the experiment.