RicardoJiang / wanandroid-compose

Compose+MVI+Navigation实现wanAndroid客户端
450 stars 78 forks source link

关于MVI UiState的疑问 #6

Closed s949492225 closed 2 years ago

s949492225 commented 2 years ago

按照mvi说法,所有的state都是集中的同一个data class中

我的疑问是既然状态不能集中,那么怎样实现状态集中而又不触发大范围的重组 ,vue和react是没有这种问题的 ,flutter有provider也可以局部更新 ,而compose难道只能铺平吗,希望大佬你有更好的方法启发一下

在传统的安卓开发中,可监测某个字段的变化局部更新。

但是在compose中由于重组是根据state的set来感知的,这样会导致state的data class任意一个字段变化都会导致所有用到state的地方重组,有性能浪费,并且界面会出现不必要的刷新,例如闪一下。

根据官方的在开发者大会上的说法: 1.状态需要铺平,不能集中到一个类里,这是一个不触发全部重组的前提。 2.状态的读取和使用和读取应该在同一个位置,即最小化返回。

1容易理解,2我用伪代码演示一下。

// 第一种 会触发全部重组
@Composable
fun Detail() {
    var count by remember { mutableStateOf(1) }

    Text("one")

    Two(count = count) //1.直接传入值,此地有读取动作

    Button(onClick = { count++ }) {
        Text("点击累加")
    }
    Text("three")
}

@Composable
fun Two(count: Int) {
    Text("$count") //2此处使用值
}
// 第二种 只在Two作用域内重组
@Composable
fun Detail() {
    var count by remember { mutableStateOf(1) }

    Text("one")

    Two { count } //1.传入一个lambda,用lazy形式获取值

    Button(onClick = { count++ }) {
        Text("点击累加")
    }
    Text("three")
}

@Composable
fun Two(getCount: () -> Int) {
    //这样读取和使用在同一个地方,而且是延迟读取,可以把重组范围缩小到Two中
    Text("${getCount()}") //2此处使用值
}
s949492225 commented 2 years ago

https://github.com/s949492225/CoolTube

这是我在搞的mvi实践的小项目,基本只有首页。

这里首页的状态比较复杂,举一个全部重组的例子

1.有一个下拉刷新,列表是有数据现在,如果此时下拉刷新触发,由于使用了一个大state存储了(正在刷新状态)和(数据列表),此时下拉刷新开始转圈,而列表因为也触发了重组,会突然一条数据都不显示,然后一瞬间再显示出来,实际的期望是旋转框重组,而数据列表不重组,待接口返回再重组。

2.实际这个页面状态流转很复杂,先加载本地显示出来,如果列表无数据则显示全局旋转框,如果列表有数据则显示下来刷新框,然后网络请求结束,如果请求有数据返回并且本地列表有数据则替换,如果请求无数据并且本地无数据则显示空页面,如果请求无数据本地有数据则弹toast不显示空页面,如果是下拉刷新并且本地有数据则显示下拉刷新而不显示全局旋转框,由于状态流转很多次,会导致页面多次大范围重组从而闪烁。

image