I run Code A and get Result A.
You will find the var temp
is always 1 when the system invoke Log.e("my", "Load $temp ${refresh.value}")
even if I have clicked the Button again and again.
You know the var temp
is wrapped with remember
, and I have assigned 2 to it in onClick
event of Button.
What's wrong with my code?
BTW, if you run Code B and it will get Result B just like I expected!
Code A
var temp = remember { 1 }
val refresh = remember { mutableStateOf(100) }
Log.e("my", "Load $temp ${refresh.value}")
Button(
onClick = {
temp
refresh.value
Log.e("my", "Save $temp ${refresh.value}")
}
) {
Text("OK $temp ${refresh.value}")
}
Result A
2022-08-29 11:05:36.825 29337-29337/info.dodata.soundmeter E/my: Load 1 100
2022-08-29 11:05:37.550 29337-29337/info.dodata.soundmeter E/my: Load 1 100
2022-08-29 11:05:39.596 29337-29337/info.dodata.soundmeter E/my: Save 2 101
2022-08-29 11:05:39.600 29337-29337/info.dodata.soundmeter E/my: Load 1 101
2022-08-29 11:05:43.274 29337-29337/info.dodata.soundmeter E/my: Save 2 102
2022-08-29 11:05:43.278 29337-29337/info.dodata.soundmeter E/my: Load 1 102
2022-08-29 11:05:52.068 29337-29337/info.dodata.soundmeter E/my: Save 2 103
2022-08-29 11:05:52.071 29337-29337/info.dodata.soundmeter E/my: Load 1 103
2022-08-29 11:05:58.509 29337-29337/info.dodata.soundmeter E/my: Save 2 104
2022-08-29 11:05:58.511 29337-29337/info.dodata.soundmeter E/my: Load 1 104
Code B
var temp = remember { 1 }
val refresh = remember { mutableStateOf(100) }
//Log.e("my", "Load $temp ${refresh.value}") //I remove it
Button(
onClick = {
temp
refresh.value
Log.e("my", "Save $temp ${refresh.value}")
}
) {
Text("OK $temp ${refresh.value}")
}
Result B
2022-08-29 11:13:30.624 31545-31545/info.dodata.soundmeter E/my: Save 2 101
2022-08-29 11:13:31.750 31545-31545/info.dodata.soundmeter E/my: Save 3 102
2022-08-29 11:13:33.003 31545-31545/info.dodata.soundmeter E/my: Save 4 103
2022-08-29 11:13:38.993 31545-31545/info.dodata.soundmeter E/my: Save 5 104
2022-08-29 11:13:40.158 31545-31545/info.dodata.soundmeter E/my: Save 6 105
CodePudding user response:
AFAIK, by the definition remember { }
is just like it's name remembering the value produced by your lambda across recomposition. When you just give it primitive value like Int
, 'String' or etc. and the remember
is recomposing in any way it will give you your primitive value inside the lambda.
On the other hand, the mutableState
or other state API's under the hood will write your last value to the parcelable and retrieve it when the recomposition occurs. So it's like a wrapper object to save last value into memory and retrieve it later using the same object remembered across recomposition by the remebber
. And I guess the Log.e("my", "Load $temp ${refresh.value}")
code is triggering the recomposition so thats why on your Code A the temp
got resetted to its return value from lambda everytime it got recomposed.
CodePudding user response:
Added one more log to explain how this code works.
@Composable
fun CodeA() {
var temp = remember {
1
}
val refresh = remember {
mutableStateOf(100)
}
Log.e("Test", "temp reset") // New added log
Log.e("Test", "Load $temp ${refresh.value}")
Button(
onClick = {
temp
refresh.value
Log.e("Test", "Save $temp ${refresh.value}")
}
) {
Text("OK $temp ${refresh.value}")
}
}
If we see, the newly added log does not have any state. It will work every time the code is executed.
Jetpack compose does optimal recomposition. So it will only recompose the code required to update the state change.
Note that only state changes will trigger recomposition.
When you have no logs outside the Button
reading refresh.value
, the recomposition will happen only for the Button
code. But once you add a new Log statement outside the Button
, the whole Composable will be recomposed to reflect the state change.
All states in an app should be defined as states. temp
should be a state as per your expectation.
Output with refresh.value
log
temp reset
Load 1 100
Save 2 101
temp reset
Load 1 101
Save 2 102
temp reset
Load 1 102
Save 2 103
temp reset
Load 1 103
Output with out refresh.value
log
temp reset
Save 2 101
Save 3 102
Save 4 103
CodePudding user response:
In Code A
1- The reason temp
never survives recomposition is it's a primitive wrapped with remember. Remember stores same object through recomposition.
What does Jetpack Compose remember actually do, how does it work under the hood?
2- The reason your whole Composable gets recomposed is because of scoped recomposition. You read refresh.value inside Button scope and in your Composable scope with log at the top
For every non-inline composable function that returns Unit, the Compose compiler generates code that wraps the function’s body in a recompose scope. When a recompose scope is invalidated, the compose runtime will ensure the (entire) function body gets recomposed (reexecuted) before the next frame. Functions are a natural delimiter for re-executable chunks of code, because they already have well-defined entry and exit points.
Jetpack Compose Smart Recomposition
Why does mutableStateOf without remember work sometimes?
https://dev.to/zachklipp/scoped-recomposition-jetpack-compose-what-happens-when-state-changes-l78