You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 18:45:46