Skip to content

SetValueFn改版

Junerver Hou edited this page Nov 29, 2024 · 3 revisions

在修改状态时,我们经常需要使用当前状态的值进行计算,将结果作为新值赋值给状态,这是一个非常常见的场景。

在过去我们使用 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

Clone this wiki locally