在Compose中像使用redux一样轻松管理全局状态

写在前面

本文中提及的use开头的函数,都出自与我的 ComposeHooks 项目,它提供了一系列 React Hooks 风格的状态封装函数,可以帮你更好的使用 Compose,无需关系复杂的状态管理,专心于业务与UI组件。

这是系列文章的第五篇,前文:

也许只有 useContextuseReducer 是不够的

书接上回,上次的文章发布后有小伙伴在评论区留言,谈到了关于在组件之间保存状态的问题,我也给予了回答,那就是使用 useContext 进行进一步的状态提升。

但是在之前版本的ComposeHooks 并没有很方便的方式让我们将 useContextuseReducer 配合起来,只使用这二者在暴露多个状态时非常麻烦。

现在全新版本的 redux-react 风格的 hook来啦,它就是 useSelectoruseDispatch,现在你可以轻松的构建全局状态,并通过这两个钩子轻松的跨组件获取状态与dispatch函数。它是基于 useContext 的进一步封装,现在你可以不需要再自己去配置 useContext 来管理状态暴露了!

如何使用

关于 reducer 的概念,请先查阅 在Compose中方便的使用MVI思想?试试useReducer!,一些概念我们就不再赘述了,这里依旧使用我最喜欢的 Todos 作为案例 (在Compose中使用状态提升?我提升个P…Provider)。

暴露全局状态

首先我们在根组件使用 ReduxProvider 组件暴露全局状态存储。

data class Todo(val name: String, val id: String)

sealed interface TodoAction
data class AddTodo(val todo: Todo) : TodoAction
data class DelTodo(val id: String) : TodoAction

val todoReducer: Reducer<List<Todo>, TodoAction> = { prevState: List<Todo>, action: TodoAction ->
    when (action) {
        is AddTodo -> buildList {
            addAll(prevState)
            add(action.todo)
        }
        is DelTodo ->  prevState.filter { it.id != action.id }
    }
}

val store = createStore {
    todoReducer with emptyList() // 使用中缀函数 with 来连接reducer函数与初始状态
}

@Composable
fun UseReduxExample() {
    ReduxProvider(store = store) {
      
    }
}

前面的代码大抵差别不大,可以看作是使用 useReducer 的 MVI 改造。

关键点在于:val store = createStore { },这里我们需要通过 createStore 函数创建一个全局的状态存储对象,在函数闭包内,我们通过中缀函数 with,连接一个 reducer函数 与一个 初始状态 .

然后我们使用 ReduxProvider(store = store) 将全局状态存储对象进行暴露。

useSelector 获取状态

现在我们的状态已经向下暴露了,因为我们已经将他提升到了 ReduxProvider 在其下的所有组件都可以使用useSelector轻松的获取状态:

@Composable
fun TodoList() {
    val todos = useSelector<List<Todo>>() //需要传递 状态 的类型
    Column {
        todos.map {
            TodoItem(item = it)
        }
    }
}

useDispatch 获取 dispatch 函数

只有状态当然是不够的,在 MVI 中我们还需要使用 dispatch函数。

在最新版本中,你可以轻松的通过 useDispatch 来进行获取:

@Composable
fun Header() {
    val dispatch = useDispatch<TodoAction>() //需要传递 Action 的类型
    val (input, setInput) = useState("")
    Row {
        OutlinedTextField(
            value = input,
            onValueChange = setInput,
        )
        TButton(text = "add") {
            dispatch(AddTodo(Todo(input, NanoId.generate())))
            setInput("")
        }
    }
}


@Composable
fun TodoItem(item: Todo) {
    val dispatch = useDispatch<TodoAction>()
    Row(modifier = Modifier.fillMaxWidth()) {
        Text(text = item.name)
        TButton(text = "del") {
            dispatch(DelTodo(item.id))
        }
    }
}

多个状态

请注意,val store = createStore { } 的闭包中可以传递多个状态,你只需要在这里通过 with 连接 reducer函数 与初始状态,就可以注册暴露多个状态到全局了:

例如:

val store = createStore {
    otherReducer with OtherData("default", 18) //另一个 reducer
    todoReducer with emptyList()
}

探索更多

项目开源地址:junerver/ComposeHooks

MavenCentral:hooks

implementation("xyz.junerver.compose:hooks:1.0.8")

欢迎使用、勘误、pr、star。

  • 32
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值