kotlin 编写:使用键记住()与derivedStateOf()

zi8p0yeb  于 2023-06-30  发布在  Kotlin
关注(0)|答案(3)|浏览(125)

这两种方法有什么区别?

  1. val result = remember(key1, key2) { computeIt(key1, key2) }(文档)
  2. val result by remember { derivedStateOf { computeIt(key1, key2) } }(文档)

两者如果key1key2都没有改变,则避免重新计算。第二个也避免了在下游状态被导出时的重新计算,但除此之外,它们的行为是相同的,不是吗?

pbossiut

pbossiut1#

derivedStateOf {}用于当您的状态或密钥更改的次数超过您想要更新UI的次数时。它就像一个缓冲器,缓冲掉你不需要的更改。这是键控remember {}derivedStateOf {}之间的主要区别。使用remember {},你仍然可以根据你的密钥变化进行重新组合,如果这是你需要的,这不是一件坏事。如果您不需要这些重组,那么derivedStateOf {}就派上用场了。
例如,仅当用户滚动LazyColumn时才显示按钮。

val isVisible = lazyListState.firstVisibleItemIndex > 0

firstVisibleItemIndex将改变0、1、2等。因为用户滚动并导致每次改变时的重新组合。
我只关心它是否为0,并且只想在条件改变时重新组合。
derivedStateOf是我的缓冲区,它缓冲了所有我不需要的额外状态更改,并将重组限制在只有derivedStateOf更改时。

val isVisible = remember { derivedStateOf { lazyListState.firstVisibleItemIndex > 0 } }

对于问题中给出的示例,正确的API是remember(key1, key2) {},而不是derivedStateOf {},因为您希望UI在密钥更改时随时更新,而没有任何更改需要缓冲。
更新:在https://www.youtube.com/watch?v=ahXLwg2JYpc&t=948s演讲中有derivedStateOf的详细解释

kx5bkwkv

kx5bkwkv2#

AFAIK这里没有区别。这只是一个巧合,两个结构在这里做同样的事情,在这个上下文中。但是,有区别!
最大的一个问题是derivedStateOf是不可组合的,它自己没有缓存(remember有)。所以derivedStateOf意味着只有在密钥更改时才必须运行的长时间计算。或者,它可以用来合并多个不可组合的状态(例如,在自定义类中)。
我认为确切的解释是模糊的“局外人”,我们需要一些输入从一些组成团队成员在这里:)。我上面的源代码是this one thread on 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 = remember {
    derivedStateOf {
        (scrollState / 100).roundToInt()
    }
}

// this will be recomposed only on counter change, so it will "ignore" scrollState in 99% of cases
Text(counter.toString()).

我的来源是直接的,因为它可以-从撰写运行时和快照系统的作者,查克Jazdzewski自己。我强烈建议在这里与他一起观看流:https://www.youtube.com/watch?v=waJ_dklg6fU
编辑2:
我们终于有了一些官方的性能文档,其中很少提到derivedStateOf。因此,derivedStateOf的官方目的是限制组合计数(如我的示例中所示)。酱料

n3h0vuf2

n3h0vuf23#

val result = remember(key1, key2) { computeIt(key1, key2) }key1key2发生变化时重新计算,但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将不会重组,因为重组需要状态更改。
例如,在某个阈值或状态之后触发另一次重组所需的输入布局或项目数量。

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
            )
        }
    }
}

相关问题