我有使用 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,
)
}
}
derivedStateOf
仅适用于 State 对象。 selectedOption
不是一个状态,因此不会检测到对其进行的任何更改。
让我们更详细地看看这个。在您原来的、仅使用
remember()
的无效示例中,derivedStateOf
在第一个合成上执行。它还执行 lambda 并跟踪所有使用的 State 对象。我们预计会是这样的:
heightValue
isOk
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
isOk
selectedOption
,因为那只是一个简单的字符串
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
,这样代码的读者就不会想知道为什么有时使用它,有时使用它来访问相同的状态。