今天大概会聊到的范围
- rememberUpdateState
上一篇聊到,SideEffect
周边还有一堆和 state & effect 相关的 API。今天想要聊聊 rememberUpdatedState
。
当今天我们要把某个资料/事件从 composable 传出来的时候,我们可以用 callback 的形式将资料往外传:
@Composable
fun MyComposable(callback: () -> Unit ) {
// childBtn
Button( onClick = { callback() }) {
Text("click")
}
}
在某些状况,callback 可能会改变。但是 callback 在还没被呼叫之前,其实都和这个 composable 无关:
@Composable
fun MyComposable(callback: () -> Unit ) {
Button( onClick = { callback() }) {
Text("click")
}
}
@Compoable
fun ParentComposable() {
Column {
var count by remember { mutableStateOf(0) }
var callback by remember { mutableStateOf({
Log.d(TAG, "MyComposable: callback init")
Unit
}) }
MyComposable(callback)
// parentBtn
Button(onClick = { count++ }) {
Text("Change")
callback = { Log.d(TAG, "MyComposable: callback $count") }
}
}
}
举个例,假设有两层的 Button,内层的 Button 点完之後会 log 一段文字。外层的 Button 每次点击的时候都会更新内层 Button 点击後 log 的文字。但因为 callback 是透过参数传入,这个参数又有被 Button 用到。因此,每次 parentBtn
点击一次,childBtn
都会触发 recomposition,即便 MyComposable
其实根本没有改变。
为了让 MyComposable
不要触发不必要的 recomposition,我们可以将 callback 给 remember
起来。remember
会在第一次 composition 执行时去运算 lambda 内的资料一次并存放起来,後面因为 Button 不是 reference 到 callback,而是 remember
後的结果,所以 callback 改变不会触发 recomposition。
@Composable
fun MyComposable(callback: () -> Unit) {
val callbackRemembered by remember { mtableStateOf(callback) }
Button(onClick = { callbackRemembered() }) {
Text("click")
}
}
不对啊,remember 记起来了就不会再改了。这样怎麽呼叫 callback 都会呼叫到同一个 callback 啊?这时候就是 rememberUpdatedState
发挥效果的时候了!
@Composable
fun MyComposable(callback: () -> Unit) {
val callbackRemembered by rememberUpdatedState(callback)
Button(onClick = { callbackRemembered() }) {
Text("click")
}
}
rememberUpdatedState
会永远记录最新的资料,但是透过 rememberUpdatedState
包装後的资料 ( callbackRemembered
) 却不会被视为修改,因此不会触发 recomposition 。
其实前面举 Button 的例子不太好,因为 rememberUpdateState
最好被使用的时机点是当这个资料需要用在 SideEffect
之中的情况。举个例:
@Composable
fun MyComposable(onDisposeCallback: () -> Unit) {
val onDisposeRem by rememberUpdatedState(newValue = onDisposeCallback)
DisposableEffect(Unit) {
// do something
onDispose {
onDisposeRem()
}
}
}
假设我们要在 dispose
的时候做某件事情,但是这件事情需要外部的人提供参数(这里是举 callback 的例子,其实不一定是 callbck 的情境),这时我们就可以透过 rememberUpdatedState
将资料记住。
那如果我们不用 remember
呢?不能单纯将参数传入并且使用吗?
@Composable
fun MyComposable(onDisposeCallback: () -> Unit) {
DisposableEffect(Unit) {
// do something
onDispose {
onDisposeCallback()
}
}
}
因为 Effect 会在首次 composition 时建立,并且在 key 值没有修改之前不会重新建立 ( 此处的 key = Unit ),因此在 onDispose
时只会触发第一次拿到的 onDisposeCallback
。若要利用 key 值改变会重建 DisposeEffect
的机制,又会让 Dispose lambda 和 onDispose lambda 没必要的重复触发。SideEffect
、LaunchedEffect
等其他的 side-effect API 也都有一样的情况。
其实仔细看看 rememberUpdatedState
其实也就是 remember
和 mutableStateOf
的组合:
@Composable
fun <T> rememberUpdatedState(newValue: T): State<T> = remember {
mutableStateOf(newValue)
}.apply { value = newValue }
差别在最後一行的 apply
,当参数改变并触发 recompose 时,rememberUpdatedState
会透过 apply
将 value 写进 state 中,达到修改 state 的效果。
今天主要是研究了 rememberUpdatedState
这个工具。有点庆幸这次我是因为看了文件发现有这个东西,要是我是直接在遇到问题时需要这个东西,感觉一定会摸不着头绪也不知道该从何查起。Side effect 和 state mangement 是 compose 中重要的大主题,感觉还有很多东西可以挖,希望之後可以多挖一点来和大家分享
Reference:
在 gradle (Module) 层级的 dependencies 中内加入: implement...
步骤一:Function Set 昨天的最後我们提到我们要找一个事後机率(Posterior Pro...
前言 背景是一个如此重要的东西,你能想像萤幕的话棉全都是白底或黑底吗!!当然不行啊!! backgr...
科技始终来自於人性 -- 「平板点餐APP」 第一次用平板点餐,是在日本京都的「啾啾烧肉」(じゅうじ...
记忆大考验 教学原文参考:记忆大考验 这篇文章会使用「阵列」积木,建立两组灯号数据,搭配「函式」、「...