这两种方法有什么区别?
val result = remember(key1, key2) { computeIt(key1, key2) }
(文档)val result by remember { derivedStateOf { computeIt(key1, key2) } }
(文档)
key1
如果两者都没有key2
改变,则两者都避免重新计算。如果下游状态是派生的,第二个也避免重新计算,但是,它们的行为是相同的,不是吗?
val result = remember(key1, key2) { computeIt(key1, key2) }
重新计算何时key1
或key2
更改,但derivedStateOf
用于跟踪一个或多个 State/MutableState 的更改,如文档中所述
var a by remember { mutableStateOf(0) }
var b by remember { mutableStateOf(0) }
val sum = remember { derivedStateOf { a + b } }
// Changing either a or b will cause CountDisplay to recompose but not trigger Example
// to recompose.
CountDisplay(sum)
当您需要跟踪 State 对象的属性变化时,使用 derivedStateOf 很方便。您存储在 State 中的值可以是一个对象,但是当您需要跟踪对象的一个或某些属性时,您需要使用 derivedStateOf。并且如果它不是从State/MutableState
带有@Stable
注释 Composable 的接口的 or 对象派生的,则不会重新组合,因为重新组合需要状态更改。
例如,您需要在某个阈值或状态之后触发另一个重组的输入布局或项目数量。
var numberOfItems by remember {
mutableStateOf(0)
}
// Use derivedStateOf when a certain state is calculated or derived from other state objects.
// Using this function guarantees that the calculation will only occur whenever one
// of the states used in the calculation changes.
val derivedStateMax by remember {
derivedStateOf {
numberOfItems > 5
}
}
Column(modifier = Modifier.padding(horizontal = 8.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "Amount to buy: $numberOfItems", modifier = Modifier.weight(1f))
IconButton(onClick = { numberOfItems++ }) {
Icon(imageVector = Icons.Default.Add, contentDescription = "add")
}
Spacer(modifier = Modifier.width(4.dp))
IconButton(onClick = { if (derivedStateMin) numberOfItems-- }) {
Icon(imageVector = Icons.Default.Remove, contentDescription = "remove")
}
}
if (derivedStateMax) {
Text("You cannot buy more than 5 items", color = Color(0xffE53935))
}
}
这是 whatsapp 文本输入,它通过读取文本来根据文本是否为空来显示图标
internal fun ChatInput(modifier: Modifier = Modifier, onMessageChange: (String) -> Unit) {
var input by remember { mutableStateOf(TextFieldValue("")) }
val textEmpty: Boolean by derivedStateOf { input.text.isEmpty() }
Row(
modifier = modifier
.padding(horizontal = 8.dp, vertical = 6.dp)
.fillMaxWidth(),
verticalAlignment = Alignment.Bottom
) {
ChatTextField(
modifier = modifier.weight(1f),
input = input,
empty = textEmpty,
onValueChange = {
input = it
}
)
Spacer(modifier = Modifier.width(6.dp))
FloatingActionButton(
modifier = Modifier.size(48.dp),
backgroundColor = Color(0xff00897B),
onClick = {
if (!textEmpty) {
onMessageChange(input.text)
input = TextFieldValue("")
}
}
) {
Icon(
tint = Color.White,
imageVector = if (textEmpty) Icons.Filled.Mic else Icons.Filled.Send,
contentDescription = null
)
}
}
}
AFAIK这里没有区别。在这种情况下,两种构造都在做同样的事情只是一个巧合。但是,有区别!
最大的一个是derivedStateOf
不可组合的,它自己没有缓存(remember
确实)。SoderivedStateOf
用于仅在密钥更改时才必须运行的长时间运行的计算。或者它可以用于合并多个不可组合的状态(例如在自定义类中)。
我认为对于“局外人”的确切解释是模糊的,我们需要一些撰写团队成员的意见:)。我上面的来源是这个 slack 上的一个线程和我自己的实验
编辑:
今天我学到了另一种derivedStateOf
用法,非常重要的一种。当使用一些非常常用的值进行计算时,它可以用来限制重组计数。
例子:
// we have a variable scrollState: Int that gets updated every time user scrolls
// we want to update our counter for every 100 pixels scrolled.
// To avoid recomposition every single pixel change we are using derivedStateOf
val counter = derivedStateOf {
(scrollState / 100).roundToInt()
}
// this will be recomposed only on counter change, so it will "ignore" scrollState in 99% of cases
Text(counter.toString()).
我的来源尽可能直接——来自 compose runtime 和快照系统的作者 Chuck Jazdzewski 本人。我强烈推荐和他一起看直播:https ://www.youtube.com/watch?v=waJ_dklg6fU