-
Notifications
You must be signed in to change notification settings - Fork 7
SetValueFn改版
在修改状态时,我们经常需要使用当前状态的值进行计算,将结果作为新值赋值给状态,这是一个非常常见的场景。
在过去我们使用 useGetState 时可以这样
val (state, setState, getState) = useGetState(default)
fun add() {
setState(getState() + 1)
}这里的 setState 就是一个 SetValueFn<T> 类型的函数,通过调用它传递一个新的值来变更状态,这很平常。
但是当我们的新状态值是基于旧状态值计算得出时,它看起来有一点不够优雅,需要调用getState() 来获取值(你也可以使用 state.value 他们是等效的)。
但是没办法,为了延时读取状态的值,避免不必要的触发重组,这是不得已的。
它与我们在 React 中的使用体验是不同的,在 React 中 useState 解构出来的 set 函数,既可以传递值也可以传递从上一个状态计算出来的函数。
在 Kotlin 2.1.0 中,我们终于可以获得和在 React 中一致的体验,现在 set 函数的签名从 SetValueFn<T> 变更为 SetValueFn<SetterEither<T>>。你的旧代码会因为类型推断报错,需要手动导入:
import xyz.junerver.compose.hooks.invoke现在你可以这样使用:
import xyz.junerver.compose.hooks.invoke
val (state, setState) = useGetState(default)
fun set(num:Int) {
setState(num) // 传递值
}
fun add(){
setState{ it +1 } // 传递函数
}这让我们的代码更加灵活!
除了手动导入函数以外:
import xyz.junerver.compose.hooks.invoke如果在过去你有使用 set 函数来传递给组件,由于签名的变更,你需要调用 left()函数,比如过去的写法:
@Composable
private fun Copy() {
val (state, setState) = useGetState("")
val (copy, _) = useClipboard()
Column {
TextField(
value = state.value,
onValueChange = setState, // 直接将setState传参
label = { Text("Text to copy") }
)
Button(onClick = { copy(state.value) }) {
Text("Copy to clipboard")
}
}
}现在你需要:
import xyz.junerver.compose.hooks.left
@Composable
private fun Copy() {
val (state, setState) = useGetState("")
val (copy, _) = useClipboard()
Column {
TextField(
value = state.value,
onValueChange = setState.left(), // 在解构出的 set 函数上调用 left 函数
label = { Text("Text to copy") }
)
Button(onClick = { copy(state.value) }) {
Text("Copy to clipboard")
}
}
}left 函数很简单,它将 (Either<T, (T)->T>)->Unit 转成 (T)->Unit
只有在 Kotlin 2.1.0 才能自动识别到我们需要的扩展函数重载,如果你仍在使用 2.1.0 之前的 kotlin 版本,除了上面的迁移,你还需要将过去调用 setValue 的代码修改为使用 Either 容器传参,需要用到left()、right() 这两个函数。
例如值类型传参,需要使用left()函数转换:
@Composable
private fun Paste() {
val (state, setState) = useGetState("") // 需要迁移
val (_, paste) = useClipboard()
Column {
Text("$state")
Button(onClick = { setState(paste()) }) {
Text("Paste from clipboard")
}
}
}需要修改为:
import arrow.core.left
@Composable
private fun Paste() {
val (state, setState) = useGetState("")
val (_, paste) = useClipboard()
Column {
Text("$state")
Button(onClick = { setState(paste().left()) }) { // 使用 `left` 函数将值转换成 Either 容器
Text("Paste from clipboard")
}
}
}使用旧值计算出新值的函数类型传参,需要使用right()函数:
val (state, setState, getState) = useGetState(0)
val async = useAsync {
delay(1.seconds)
setState(getState()+ 1)
}修改为:
import arrow.core.right
val (state, setState) = useGetState(0)
val async = useAsync {
delay(1.seconds)
setState({ it: Int -> it + 1 }.right())
}