I think the running result of either Code A or Code B will be the same, why do I need to use derivedStateOf
in Code A?
Code A
var age by remember { mutableStateOf(1) }
val person by remember {
derivedStateOf { "my age is $age" }
}
Column {
Button(onClick = {
age = 1
}) {
Text(text = "click add age")
}
Text(text = person)
}
Code B
var age by remember { mutableStateOf(1) }
val person= "my age is $age"
Column {
Button(onClick = {
age = 1
}) {
Text(text = "click add age")
}
Text(text = person)
}
CodePudding user response:
please check this link: Compose: remember() with keys vs. derivedStateOf()
and this one: https://developer.android.com/jetpack/compose/performance#use-derivedstateof
Derived state lets you tell Compose which changes of state actually should trigger recomposition. In this case, specify that you care about when the first visible item changes. When that state value changes, the UI needs to recompose
CodePudding user response:
derivedState
is for observing one or multiple State
s together when specific conditions are met and you use it to not read or trigger recomposition every time State
you read changes. Of course you can listen for changes in non-State object but since updating them won't trigger any recompositions it's useless to use derivedState with a non-state object in my opinion. Advantage of using derived state is reading a State that might change in every frame and not hindering performance
Most common example is listening for LazyListState
to show Floating action button like in messaging apps.
val showButton by remember {
derivedStateOf { scrollState.firstVisibleItemIndex != 0 }
}
if (showButton) {
FloatingActionButton(
onClick = {
coroutineScope.launch {
scrollState.animateScrollToItem(0)
}
},
) {
// Implementation
}
}
If you don't use derivedStateOf in this example you might experience lag issues when scrolling.
A simple example
class MyClass() {
var counter = 0
var counterState = mutableStateOf(0)
var stringState = mutableStateOf("")
}
Column(modifier = Modifier.fillMaxSize()) {
val myObject = remember { MyClass() }
val counterDerivedState = derivedStateOf { myObject.counter > 5 }
val counterDerivedStateFromState =
derivedStateOf { myObject.counterState.value > 5 }
Button(onClick = { myObject.counter = 1 }) {
Text(
"Counter: ${myObject.counter}"
)
}
Text(
"Counter: ${myObject.counter} Stateful counter: ${myObject.counterState.value}",
color = if (counterDerivedState.value) Color.Green else Color.Red
)
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = { myObject.counterState.value = 1 }) {
Text(
"Counter: ${myObject.counterState.value}"
)
}
Text(
"Counter: ${myObject.counterState.value}",
color = if (counterDerivedStateFromState.value) Color.Green else Color.Red
)
val texAndCounterDerivedState = derivedStateOf {
myObject.counterState.value > 0 &&
myObject.stringState.value.isNotEmpty()
}
Spacer(modifier = Modifier.height(20.dp))
TextField(
value = myObject.stringState.value,
onValueChange = {
myObject.stringState.value = it
}
)
Text(
"Counter: ${myObject.counterState.value}, String: ${myObject.stringState.value}",
color = if (texAndCounterDerivedState.value) Color.Green else Color.Red
)
}
We derive states from a non-State variable myObject.counter which doesn't trigger recomposition when its value changes, one from myObject.counterState and combined State of
var counterState = mutableStateOf(0)
var stringState = mutableStateOf("")
If you touch first button you can observe that myObject.counter is increased but since it doesn't trigger a recomposition you don't see it on screen or change of derivedState when it happens.