Compose 重组和无状态

  1. Compose的重组和Remember
  2. 无状态
  3. 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方法内部不会对数据做任何修改。这也为,单一信息源的实现提供的基础。

results matching ""

    No results matching ""