写在前面
本文中提及的 use
开头的函数,都出自与我的 ComposeHooks 项目,它提供了一系列 React Hooks 风格的状态封装函数,可以帮你更好的使用 Compose,无需关心复杂的状态管理,专心于业务与UI组件。
这是系列文章的第6篇,前文:
useSelector、useDispatch 足够好用么?
在上一次更新中,为了解决全局状态的管理问题,我们引入了新的钩子:useSelector
、useDispatch
它们的源码非常简单
1 2 3 4 5 6 7 8 9 10 11
| @Composable inline fun <reified T> useSelector(): T { val map = useContext(context = ReduxContext) return map.first[T::class] as T }
@Composable inline fun <reified A> useDispatch(): Dispatch<A> { val map = useContext(context = ReduxContext) return map.second[A::class] as Dispatch<A> }
|
得力于 kotlin 的 inline
、reified
关键字,我们可以轻松的从store中取出我们的状态、以及dispatch函数。
但是有时我们并不需要整个状态对象,我们可能只需要其中部分成员属性,亦或者需要对状态中的某个属性进行变形映射。
说到这里不知道你有没有想到什么?还记得么你可能一直在kt文件中写Java代码?没错就是 run
映射
更好用的 useSelector
我们只需要简单的构建一个重载函数,就可以让 useSelector
变得更好用:
1 2
| @Composable inline fun <reified T, R> useSelector(block: T.() -> R) = useSelector<T>().run(block)
|
现在我们继续对之前例子的代码进行改造:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Composable private fun SubSimpleDataStateText() {
val name = useSelector<SimpleData, String> { name } Text(text = "User Name: $name") }
@Composable private fun SubSimpleDataStateText2() { val age = useSelector<SimpleData, String> { "age : $age" } Text(text = "User $age") }
|
更好用的 useDispatch
dispatch 函数非常非常简单:typealias Dispatch<A> = (A) -> Unit
1
| { action: Any -> setState(reducer(state, action)) }
|
但是在异步场景,使用它有一点点麻烦,例如一个网络请求的场景:
1 2 3 4 5 6 7 8 9
| val scope = rememberCoroutineScope() TButton(text = "changeName") { scope.launch { delay(1.seconds) val result = dispatch(SimpleAction.ChangeName(result)) } }
|
多多少少我们要写一点模板代码;
我们继续对 useDispatch
进行改造,增加一个异步版本的:
1 2 3 4 5 6 7 8 9 10 11 12
| typealias DispatchAsync<A> = (block: suspend CoroutineScope.() -> A) -> Unit
@Composable inline fun <reified A> useDispatchAsync(): DispatchAsync<A> { val dispatch: Dispatch<A> = useDispatch() val asyncRun = useAsync() return { block -> asyncRun { dispatch(block()) } } }
|
改造后的 useDispatchAsync
函数将会返回一个异步版本的 dispatch 函数,函数闭包的返回值将会作为 Action 进行 dispatch 操作。
那么上边的模板代码将会变成:
1 2 3 4 5 6 7 8
| val asyncDispatch = useDispatchAsync<SimpleAction>() TButton(text = "Async changeName") { asyncDispatch { delay(1.seconds) val result = SimpleAction.ChangeName(result) } }
|
使用新的 hook 改造你的 retrofit 请求获得全局状态
如果你使用 retrofit ,并且已经使用协程改造了网络请求,你甚至可以将请求结果作为Action,那么这里将会进一步简化
一个极简的例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| sealed interface NetFetchResult { data class Success(val data: String, val code: Int) : NetFetchResult data class Error(val msg: Throwable) : NetFetchResult data object Idle : NetFetchResult data object Loading : NetFetchResult }
val fetchReducer: Reducer<NetFetchResult, NetFetchResult> = { _, action -> action }
val store = createStore { fetchReducer with NetFetchResult.Idle }
@Composable fun UseReduxFetch() { val fetchResult: NetFetchResult = useSelector() val dispatchAsync = useDispatchAsync<NetFetchResult>() Column { Text(text = "result: $fetchResult") TButton(text = "fetch") { dispatchAsync { delay(2.seconds) NetFetchResult.Success("success", 200) } } } }
|
探索更多
好了以上就是 hooks 1.0.9 版本带来的一点小小改动,现在你的全局状态可以更加轻松的管理与使用了!
项目开源地址:junerver/ComposeHooks
MavenCentral:hooks
1
| implementation("xyz.junerver.compose:hooks:1.0.9")
|
欢迎使用、勘误、pr、star。