衍生状态没有响应。帮我理解为什么

问题描述 投票:0回答:1

我有使用 3 种不同状态的衍生状态:

heightValue
isOk
selectedOption
,它们在
(selectedOption, onOptionSelected)
中被解构,可能就是原因。除非我在
selectedOption
中指定,否则它不会响应
remember(selectedOption)
中的更改。我试图理解为什么以及我的修复是否是正确的修复,或者我应该以不同的方式处理解构。

val radioOptions = listOf("A", "B", "C", "D")
var heightValue by remember { mutableIntStateOf() }
var isOk by remember { mutableStateOf(true) }
val (selectedOption, onOptionSelected) = remember { mutableStateOf(radioOptions[0]) }

//val calculatedValue by remember() { /*This won't work. Next line is the fix*/
val calculatedValue by remember(selectedOption) {
    derivedStateOf {
        calcNewValueInch(
            pickerValue = heightValue,
            isOk = isOk,
            selectedOption = selectedOption,
        )
    }
}
kotlin android-jetpack-compose
1个回答
0
投票

derivedStateOf
仅适用于 State 对象。
selectedOption
不是一个状态,因此不会检测到对其进行的任何更改。

让我们更详细地看看这个。在您原来的、仅使用

remember()
的无效示例中,
derivedStateOf
在第一个合成上执行。它还执行 lambda 并跟踪所有使用的 State 对象。我们预计会是这样的:

  1. heightValue
  2. isOk
  3. selectedOption

然后

derivedStateOf
的结果是
remember
ed,因此重组时不会再次执行。没关系:当上面列表中的任何状态发生变化时,现在创建的派生状态会自动更新。

除了仅适用于 1. 和 2. 之外,不适用于 3.

由于

derivedStateOf
仅跟踪其 lambda 中使用的实际 State 对象,因此很明显,当 3. 更改时它不会更新:
selectedOption
只是一个简单的字符串,而不是状态。更有趣的问题是为什么它确实适用于 1. 和 2.,因为它们没有状态,只是一个 Int 和一个 Boolean。但这并不完全正确。它们实际上是您使用
by
关键字检索的委托。这意味着您可以将它们用作简单的 Int 和 Boolean 值,但每当读取它们的值时,实际上会执行委托对象的
getValue
函数。当它们的值被设置时,将调用
setValue
。 Kotlin 向您隐藏了所有这些,因此您的代码看起来更干净,但这就是访问委托时实际发生的情况。总之,1. 和 2. 是某个状态的“代表”,因此 derivedStateOf 可以看到这些状态并监控它们的变化。
然而,

selectedOption

是通过解构 MutableState 创建的。这样您就得到了一个简单的字符串,其中包含解构时的值。棘手的是,对于大多数情况来说,这已经足够了,所以感觉与授权州没有太大不同:

Text(selectedOption) 

这将按预期工作,并在每次 
selectedOption

的基础状态发生更改时更新。这是因为 Compose 运行时会跟踪所有状态读取,并且解构状态也如此计数。当 State 改变时,它会重新组合函数,因此重复解构并更新

selectedOption
。但
derivedStateOf
位于
remember
后面,并且不受此类重组的影响。这就是为什么它会跟踪所有 State 对象本身。但它从来没有看到
selectedOption
背后的State的解构,所以它只看到结果,这是一个简单的String,而不是一个State。
我们现在可以修改上面 

derivedStateOf

跟踪的状态列表:


    heightValue
  1. 委托背后的状态对象
  2. isOk
  3. 委托背后的状态对象
  4. 不是
  5. selectedOption,因为那只是一个简单的字符串
    
    
  6. 在 lambda 中访问的唯一状态是 1. 和 2.,因此无论您对 3. 做什么,都不会影响
derivedStateOf

当您使用 

remember(selectedOption)

时,它似乎可以工作,但实际发生的情况是,每次

selectedOption
更改并创建一个新状态时,整个派生状态都会被丢弃。再次执行 lambda,并创建上面应该观察变化的 State 对象列表,但 3. 实际上不在列表中并不重要,因为您只需创建一个
new
派生 State 对象即可更改时,您不会让 current 派生的 State 对象更新。 问题的正确解决方案是让国家留在

selectedOption

周围。要么像您对 1. 和 2. 所做的那样进行委托,或者如果出于某种原因您不想这样做,则可以通过以下方式进行:

val selectedOptionState = remember { mutableStateOf(radioOptions[0]) }
val (selectedOption, onOptionSelected) = selectedOptionState

val calculatedValue by remember {
    derivedStateOf {
        calcNewValueInch(
            pickerValue = heightValue,
            isOk = isOk,
            selectedOption = selectedOptionState.value, // this accesses the State now
        )
    }
}

在这种情况下,我建议您完全删除解构,只访问 
selectedOptionState.value

,这样代码的读者就不会想知道为什么有时使用它,有时使用它来访问相同的状态。

    

© www.soinside.com 2019 - 2024. All rights reserved.