Jetpack Compose中MutableStateOf列表修改非构造属性不触发重组
问题:Jetpack Compose中修改data class非构造属性无法触发重组的原因
在Jetpack Compose开发中会遇到这样的情况:当修改被观察列表内data class的非构造属性时,无法触发UI重组;但修改该data class的构造属性时,却能正常触发重组。以下是两种场景的代码示例:
无法触发重组的代码示例(sadProperty未在构造函数中声明):
data class IntWrapper(val actualInt: Int = 0) { var sadProperty: Int = 0 } @Preview @Composable fun test() { var state by remember { mutableStateOf(listOf(IntWrapper(1), IntWrapper(2), IntWrapper(3),IntWrapper(4)))} fun onClick(item: IntWrapper) { val indexOf = state.indexOf(item) val newState = state.minus(item).toMutableList() val copy = item.copy() copy.sadProperty = Random.nextInt() newState.add(indexOf, copy) state = newState } Column() { for (item in state) { Text("ac: ${item.actualInt} sad: ${item.sadProperty}", modifier = Modifier.clickable { onClick(item)}) } } }
可以触发重组的代码示例(actualInt在构造函数中声明):
data class IntWrapper(var actualInt: Int = 0) { var sadProperty: Int = 0 } @Preview @Composable fun test() { var state by remember { mutableStateOf(listOf(IntWrapper(1), IntWrapper(2), IntWrapper(3),IntWrapper(4)))} fun onClick(item: IntWrapper) { val indexOf = state.indexOf(item) val newState = state.minus(item).toMutableList() val copy = item.copy() copy.actualInt = Random.nextInt() newState.add(indexOf, copy) state = newState } Column() { for (item in state) { Text("ac: ${item.actualInt} sad: ${item.sadProperty}", modifier = Modifier.clickable { onClick(item)}) } } }
原因分析
核心逻辑在于Compose的重组触发机制,以及Kotlin data class的特性:
- Compose的
mutableStateOf通过对比新旧值的相等性来决定是否触发重组。当你给state赋值新列表时,Compose会逐一对比新旧列表中元素的相等性,判断状态是否真的变化。 - Kotlin的data class会自动基于构造函数中声明的属性生成
equals和hashCode方法。也就是说,两个data class对象只有当构造属性完全一致时,才会被判定为相等。 - 第一个示例中:复制IntWrapper对象后修改的是非构造属性
sadProperty,但构造属性actualInt没有变化,所以复制后的对象和原对象调用equals会返回true。新列表替换原列表后,Compose对比发现所有元素都和原来相等,认为状态未改变,因此不触发重组。 - 第二个示例中:复制对象后修改了构造属性
actualInt,此时复制后的对象和原对象的equals结果为false。新列表和原列表对比时,对应位置的元素不相等,Compose判定状态发生了变化,就会触发UI重组更新显示。
如果想让非构造属性的修改也能触发重组,有两种方案:
- 将该属性移到data class的构造函数中,让它参与自动生成的
equals判断。 - 手动重写data class的
equals和hashCode方法,把非构造属性也纳入相等性判断的范围。
内容的提问来源于stack exchange,提问作者TickL Me Tibbers
相关产品推荐
相关产品推荐

