Android Jetpack Compose mutableStateListOf 不进行重组
Android Jetpack Compose mutableStateListOf not doing Recomposition
所以,我在 viewModel
中有一个 mutableStateListOf
:
var childTravellersList = mutableStateListOf<TravellersDetails>()
TravellersDetails
是一个数据 class 有一个名为 error
.
的字段
这个 childTravellersList
在 UI 中用作:
val list = remember{viewModel.childTravellersList}
LazyColumn(state = lazyColumnState) {
itemsIndexed(list) { index, item ->
SomeBox(show = if(item.error) true else false)
}
}
我在 viewModel
中编写了一个函数,它在给定 childTravellersList
的 index
处更新 error
的 TravellersDetails 为:
fun update(index){
childTravellersList[index].error = true
}
因此,无论何时我调用此函数,列表都应该得到更新。
这会更新列表,但 UI 不会触发重组。
我哪里做错了?
仅当您更改列表本身时才会发生重组。你可以这样做。
var childTravellersList by mutableStateOf(emptyList<TravellersDetails>())
fun update(indexToUpdate: Int) {
childTravellersList = childTravellersList.mapIndexed { index, details ->
if(indexToUpdate == index) details.copy(error = true)
else details
}
}
此外,您无需像在此处所做的那样在可组合项中记住此列表 val list = remember{viewModel.childTravellersList}
。由于 MutableState 位于视图模型内部,因此它将始终在所有重组中幸存下来。只需在 LazyColumn 中使用 viewModel.childTravellersList
。
mutableStateListOf
只能通知 adding/removing/replacing 列表中的某个元素。当您更改列表中的任何 class 时,可变状态无法知道它。
数据classes非常适合在单向数据流中存储不可变状态,因为你总是可以用copy
“改变”它,同时你看到需要将新数据传递给查看或可变状态。因此,避免将 var
变量与数据 classes 一起使用,始终将它们声明为 val
以防止此类错误。
var childTravellersList = mutableStateListOf<TravellersDetails>()
fun update(index){
childTravellersList[index] = childTravellersList[index].copy(error = true)
}
另一个问题是您正在使用 val list = remember{viewModel.childTravellersList}
:它会保存第一个列表值并防止将来更新。有了ViewModel就可以直接使用了itemsIndexed(viewModel.childTravellersList)
使用单一触发机制来更新您的可组合项。您的列表应该仅作为普通变量提供。您可以 add/remove/delete 列表中的项目,甚至可以更新每个项目的属性。对列表进行这些更新后,您可以通过生成一个随机数来重组您的可组合项,该随机数绑定到在您的可组合项中观察到的可变状态可观察项:
科特林:
class MyViewModel: ViewModel() {
var childTravellersList = mutableListOf<TravellersDetails>()
var onUpdate = mutableStateOf(0)
private fun updateUI() {
onUpdate.value = (0..1_000_000).random()
}
fun update(index){
childTravellersList[index].error = true
updateUI()
}
}
@Composable
fun MyComposableHandler() {
// This will detect any changes to data and recompose your composable.
viewmodel.onUpdate.value
MyComposable(
travelersList = viewmodel.childTravellersList
)
}
@Composable
fun MyComposable(
travelersList: List<TravellersDetails>
) {
}
您应该避免在视图模型中为需要更新的不同变量创建多个可变状态变量。完全没有必要这样做。只需创建一个可变状态变量,如上所示。每当您需要重构可组合项时,只需更新 UI 函数即可。视图模型中的逻辑应该决定需要更新什么,但将实际的更新机制留给可观察到的单个可变状态。这是一个干净的模式,将使您的可组合项更新更加容易。
但是,请记住,您的 UI 通常由层次结构中的许多可组合项组成。当只有一个元素发生变化时,您不希望重构整个层次结构。出于这个原因,可变状态可观察对象应该用于每个需要独立于层次结构中的其他组合的可组合项。
使用上面显示的解决方案的另一个好处是您可以轻松更新对象而无需创建新对象。如果您希望您的可组合项仅在对象的某个 属性 发生变化时进行重组,则不能使用可变状态可观察对象,因为它们仅检测对象本身的变化,而不检测对象属性的变化。这就是为什么您最好使用上面显示的触发方法,并在可组合项重组时简单地检索更新的对象。
所以,我在 viewModel
中有一个 mutableStateListOf
:
var childTravellersList = mutableStateListOf<TravellersDetails>()
TravellersDetails
是一个数据 class 有一个名为 error
.
这个 childTravellersList
在 UI 中用作:
val list = remember{viewModel.childTravellersList}
LazyColumn(state = lazyColumnState) {
itemsIndexed(list) { index, item ->
SomeBox(show = if(item.error) true else false)
}
}
我在 viewModel
中编写了一个函数,它在给定 childTravellersList
的 index
处更新 error
的 TravellersDetails 为:
fun update(index){
childTravellersList[index].error = true
}
因此,无论何时我调用此函数,列表都应该得到更新。
这会更新列表,但 UI 不会触发重组。 我哪里做错了?
仅当您更改列表本身时才会发生重组。你可以这样做。
var childTravellersList by mutableStateOf(emptyList<TravellersDetails>())
fun update(indexToUpdate: Int) {
childTravellersList = childTravellersList.mapIndexed { index, details ->
if(indexToUpdate == index) details.copy(error = true)
else details
}
}
此外,您无需像在此处所做的那样在可组合项中记住此列表 val list = remember{viewModel.childTravellersList}
。由于 MutableState 位于视图模型内部,因此它将始终在所有重组中幸存下来。只需在 LazyColumn 中使用 viewModel.childTravellersList
。
mutableStateListOf
只能通知 adding/removing/replacing 列表中的某个元素。当您更改列表中的任何 class 时,可变状态无法知道它。
数据classes非常适合在单向数据流中存储不可变状态,因为你总是可以用copy
“改变”它,同时你看到需要将新数据传递给查看或可变状态。因此,避免将 var
变量与数据 classes 一起使用,始终将它们声明为 val
以防止此类错误。
var childTravellersList = mutableStateListOf<TravellersDetails>()
fun update(index){
childTravellersList[index] = childTravellersList[index].copy(error = true)
}
另一个问题是您正在使用 val list = remember{viewModel.childTravellersList}
:它会保存第一个列表值并防止将来更新。有了ViewModel就可以直接使用了itemsIndexed(viewModel.childTravellersList)
使用单一触发机制来更新您的可组合项。您的列表应该仅作为普通变量提供。您可以 add/remove/delete 列表中的项目,甚至可以更新每个项目的属性。对列表进行这些更新后,您可以通过生成一个随机数来重组您的可组合项,该随机数绑定到在您的可组合项中观察到的可变状态可观察项:
科特林:
class MyViewModel: ViewModel() {
var childTravellersList = mutableListOf<TravellersDetails>()
var onUpdate = mutableStateOf(0)
private fun updateUI() {
onUpdate.value = (0..1_000_000).random()
}
fun update(index){
childTravellersList[index].error = true
updateUI()
}
}
@Composable
fun MyComposableHandler() {
// This will detect any changes to data and recompose your composable.
viewmodel.onUpdate.value
MyComposable(
travelersList = viewmodel.childTravellersList
)
}
@Composable
fun MyComposable(
travelersList: List<TravellersDetails>
) {
}
您应该避免在视图模型中为需要更新的不同变量创建多个可变状态变量。完全没有必要这样做。只需创建一个可变状态变量,如上所示。每当您需要重构可组合项时,只需更新 UI 函数即可。视图模型中的逻辑应该决定需要更新什么,但将实际的更新机制留给可观察到的单个可变状态。这是一个干净的模式,将使您的可组合项更新更加容易。
但是,请记住,您的 UI 通常由层次结构中的许多可组合项组成。当只有一个元素发生变化时,您不希望重构整个层次结构。出于这个原因,可变状态可观察对象应该用于每个需要独立于层次结构中的其他组合的可组合项。
使用上面显示的解决方案的另一个好处是您可以轻松更新对象而无需创建新对象。如果您希望您的可组合项仅在对象的某个 属性 发生变化时进行重组,则不能使用可变状态可观察对象,因为它们仅检测对象本身的变化,而不检测对象属性的变化。这就是为什么您最好使用上面显示的触发方法,并在可组合项重组时简单地检索更新的对象。