简体   繁体   English

撰写:remember() with keys vs. derivedStateOf()

[英]Compose: remember() with keys vs. derivedStateOf()

What is the difference between these two approaches?这两种方法有什么区别?

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

Both avoid re-computation if neither key1 nor key2 has changed.如果key1key2都没有改变,两者都避免重新计算。 The second also avoids re-computations if downstream states are derived, but else, they are identical in their behavior, aren't they?如果下游状态是派生的,第二个也可以避免重新计算,但是,它们的行为是相同的,不是吗?

derivedStateOf {} is used when your state or key is changing more than you want to update your UI .当您的 state 或密钥更改超过您想要更新 UI 时,使用derivedStateOf {} It acts as a buffer for you, buffering out the changes you don't need.它充当您的缓冲区,缓冲您不需要的更改。 That is the primary difference between a keyed remember {} and derivedStateOf {} .这是 keyed remember {}derivedStateOf {}之间的主要区别。 With remember {} , you will still recompose as much as your key changes, which isn't a bad thing if that's what you need.使用remember {} ,您仍然会尽可能多地重构您的密钥更改,如果这是您需要的,这并不是一件坏事。 If you don't need those recompositions though, that is where derivedStateOf {} comes in.但是,如果您不需要这些重组,那就是derivedStateOf {}的用武之地。

Take for example showing a button only if the user has scrolled a LazyColumn .例如,仅当用户滚动了LazyColumn时才显示按钮。

val isVisible = lazyListState.firstVisibleItemIndex > 0

firstVisibleItemIndex will change 0, 1, 2 etc as the user scrolls and cause a recomposition for every time it changes. firstVisibleItemIndex将在用户滚动时更改 0、1、2 等,并在每次更改时重新组合。

I only care about if it's 0 or not 0 and only want to recompose when that condition changes.我只关心它是否为 0,并且只想在条件发生变化时重新组合。

derivedStateOf is my buffer, it's buffering out all of those extra state changes I don't need and limiting the recomposition to only when the derivedStateOf changes. derivedStateOf是我的缓冲区,它缓冲了我不需要的所有额外的 state 更改,并将重组限制为仅在derivedStateOf更改时。

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

For the example given in the question, a remember(key1, key2) {} is the correct API to use there, not derivedStateOf {} because you want your UI to update any time the key changes, there isn't any change to buffer out.对于问题中给出的示例, remember(key1, key2) {}是正确的 API 在那里使用,而不是derivedStateOf {}因为您希望您的 UI 在键更改时更新,缓冲区没有任何更改出去。

AFAIK there is no difference here. AFAIK这里没有区别。 It's just a coincidence that both constructs are doing the same thing here in this context .在这种情况下,两种构造都在做同样的事情只是一个巧合。 But, there are differences!但是,有区别!

The biggest one is that derivedStateOf is not composable and it does no caching on it's own ( remember does).最大的一个是derivedStateOf是不可组合的,并且它自己没有缓存( remember )。 So derivedStateOf is meant for long running calculations that have to be run only if key changes.因此, derivedStateOf用于仅在密钥更改时才必须运行的长时间运行的计算。 Or it can be used to merge multiple states that are not in composable (in custom class for example).或者它可以用于合并多个不可组合的状态(例如在自定义 class 中)。

I think the exact explanation is blurred for "outsiders", we need some input from some compose team member here:).我认为“局外人”的确切解释是模糊的,我们需要一些撰写团队成员的意见:)。 My source for the above is this one thread on slack and my own experiments我上面的来源是这个 slack 上的一个线程和我自己的实验

Another usage for derivedStateOf which is practical is when you need to track changes in one mutableState to have value about another. derivedStateOf 的另一种实用用法是当您需要跟踪一个 mutableState 中的更改以对另一个 mutableState 有价值时。

For instance an Input layout or number of items that you need to trigger another recomposition after a certain threshold or state.例如,您需要在某个阈值或 state 之后触发另一个重组的输入布局或项目数。

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

This is whatsapp text input that displays icons based on whether text is empty or not by reading from text这是 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
            )
        }
    }
}

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM