Compose 重组和无状态
- Compose的重组和Remember
- 无状态
- Compose的单一信息源和单向数据流原则
重组和Remember
先看这么个小例子,猜测界面输出的结果会是什么?
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
UseRemember()
}
}
@Composable
fun UseRemember() {
Log.d(TAG, "UseRemember: ")
var name by mutableStateOf("compose")
Text("name is $name")
lifecycleScope.launch {
delay(3000)
name = "Recompose"
}
}
期望的name结果是Recompose,但是界面显示的文本却是:
name is compose
并且有趣的是,该方法的日志会不停打印UseRemember
。这是为什么呢???
这是因为发生了重组(Recompose),重组是指输入更改时再次调用可组合函数的过程。
当使用协程实现3s后更新Text,输入的name改变,此时发生会重组,导致UseRemember
方法再次执行。
所以name
就会不断被初始化为compose
,方法日志也不断被打印。
那怎么才能解决这个奇葩的问题呢?
@Composable
fun UseRemember() {
var name by remember {
mutableStateOf("compose")
}
Text("name is $name")
Log.d(TAG, "UseRemember: ")
lifecycleScope.launch {
delay(3000)
name = "Recompose"
}
}
对输入的变量使用Remember
关键字,Remember
中文是记住,它的作用也类似,它会存储变量第一次赋值的结果,之后如果使用该变量时会去获取记录的值,之后如果值没有变化也就不会再次发生重组,所以UseRemember
方法也就不会被重复调用了。
无状态
无状态(Stateless):这里的状态是指控件属性,简单点说,控件本身的属性不被共享,外部没法获取到。
比如下面的Stateless
方法里的name
属性,该方法外就没法获取该属性:
@Composable
fun Stateless() {
var name by remember {
mutableStateOf("compose")
}
Text("name is $name")
}
那怎么办呢?改写法,让变量共享出去,但这并意味指全局变量共享!比如改写成这样:
@Composable
fun Stateless(name: String) {
Text("name is $name")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val name by remember {
mutableStateOf("compose")
}
Stateless(name)
}
}
很简单,以参数的形式传入,这个操作装逼一点叫做:状态提升。就是说把属性提升一层,这样就能再提升的那一层及下层使用,同时并不是作为全局参数,避免了全局修改带来的风险。
单一信息源
官方建议遵循单一信息源(Single Source of Truth)原则,可以解决多信息来源的同步问题。它是Compose
设计的一个重要原则。比如编辑文本框TextField
的设计
TextField
初次使用TextField
会感觉奇怪的点是,为什么输入新的文本不会立刻更新,必须得手动添加赋新值的代码?
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
var value by remember {
mutableStateOf("abc")
}
materialUI(value = value, onValueChange = { newValue ->
// 必须手动赋值
value = newValue
})
}
@Composable
fun materialUI(value: String, onValueChange: (String) -> Unit){
TextField(value = value, onValueChange = onValueChange)
}
其实这里面就遵循了单一信息源的原则,TextField
内部并不会对外部的数据做任何更改,这就使数据源变得更简单,不会造成内部的修改引发的外部不可预期的问题。
单向数据流
Compose
还遵循了单向数据流的原则,在前面提到的状态提升就是对它的体现,可以看到数据是自上往下传输的,Composable
方法内部不会对数据做任何修改。这也为,单一信息源
的实现提供的基础。