React 状态管理:从现在开始拥抱redux-toolkit
携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情
在之前的文章:从零开始学习React-5:状态与状态管理,我们简单的介绍目前最流行的状态管理库 redux。今天我们介绍由 redux 团队推出的,可以更高效、更方便使用 redux 的另一个工具:Redux-Toolkit(简称:RTK)
为什么是 redux-toolkit
redux 被设计的非常灵活,这也带来了一些问题,例如如果我们直接使用 redux 会有如下几点不便之处:
模板代码太多
在上一篇文章示例中可能感觉还不明显,其实正常项目中,每一个 Action 一般都会有这样几个模板代码:
- 声明一个静态的 type 字符串,例如
const ADD_TODO = 'addTodo';
- 声明一个 action creator 函数,例如
export const addTodo = (data) => ({ type: ADD_TODO, data });
- reducer 函数内需要根据 action 的 type 写
switch - case
action 对象的 type 一般都是唯一的一个字符串,因为 dispatch 一个 action 时,所有的 reducer 函数都会收到。
这带来了一个隐藏的问题,如果我们在多个 reducer 函数中 case 了 一个相同的 type 字符串,这两个 reducer 都会执行动作,改变状态。
这也就是为什么 Redux 推荐我们使用 action creator 函数,这可以有效的避免写错导致bug。
- 声明一个静态的 type 字符串,例如
仅安装 redux 可能无法胜任。我们一般还需要安装 比如 redux-thunk(异步)、immer(状态不可变) 等库来配合 redux 工作。
这就导致了 redux 的配置比较复杂,很难做到开箱即用,即使有的配置已经成为了某种意义上的 “ 最佳实践 ”。
RTK 通过几个函数很好的帮我们解决了这些细节问题,几乎做到了开箱即用。
如何使用 RTK
这里我们还是使用之前文章的 TodoList 来作为例子,我们将它修改成使用 RTK 实现。
安装 RTK :
yarn add @reduxjs/toolkit react-redux
查看其依赖我们可以看到 RTK 已经依赖了 redux 与 redux-thunk了
1
2
3
4
5
6"dependencies": {
"immer": "^9.0.7",
"redux": "^4.1.2",
"redux-thunk": "^2.4.1",
"reselect": "^4.1.5"
},创建
todo.slice.js
Slice 切片
1 | import { createSlice } from "@reduxjs/toolkit"; |
这里我们按照官方推荐使用 ES6 解构和导出语法,来获取 action creator 与 reducer。
从代码可以看出,通过 createSlice
函数,我们减少了绝大多数的模板代码,我们无需再写 type、action creator,这一切 RTK 都帮我们在背后默默实现了。
而且我们无需再关注状态不可变,我们只需像操作普通对象一样,在 reducers 中的 case 函数中随意操作对象,这一切都被 immer 帮我们搞定了。
你可能会好奇,最终通过 action creator 函数返回的是一个这样的 action 对象:
1 | {type: 'todo/addTodo', payload: ...} |
我们在 reducers 中写的 case 中拿到的 action 就是这样的固定格式,这一定需要注意,因为在我们过去使用 redux 时,并没有规范 action 对象的类型。
ps:可以看到 RTK 自动的帮我们将 type 命名为了 name + case函数名
这样的格式,这对于我们调试也是非常方便的,起名困难症表示 RTK 赛高!
注意:其实代码修改到这里,我们的程序就可以正常运行了,这也是 RTK 的一大优势,我们可以不必全部重新改造我们的程序,它没有增加我们的心智负担,并不需要我们重新去掌握一个库,而是通过这些工具性质的函数,帮助我们更好的使用 Redux。
- 使用
configureStore
函数创建 store 对象
1 | import { configureStore } from "@reduxjs/toolkit" |
configureStore
函数接收一个配置redux的对象作为参数,具有如下选项:
reducer
必选参数,可以是一个 reducer 函数也可以是一个由 slice reducer 构成的对象如果是 reducer 函数,就直接将他作为根 reducer,如果是一个对象,就自动帮我们调用
combineReducers
函数来合并多个 reducer 创建根 reducer 函数middleware
中间件devTools
是否启用 Redux DevTools
更多配置,请查看文档:https://redux-toolkit.js.org/api/configureStore
剩余的使用是与原来一模一样的,我们无需改动其他代码!
其他 api 介绍
除了我们上面介绍的这种比较完整的改造,其实 RTK 还提供了很多粒度更小的 api,来方便我们改造原有的模板代码,这里我们简单介绍几个:
1. createAction
这个函数可以帮我们减少创建 action 时的模板代码,还有我们的例子来说明:
1 | // before |
我们完全不需要再写 action creator 函数了,直接使用 createAction
就可以帮我们创建,它的源码其实也很简单:
1 | export function createAction(type: string, prepareAction?: Function): any { |
2.createReducer()
这个函数可以帮我们简化 reducer 函数的创建,并在其内部使用了 immer ,让我们可以直接操作 state,而不用再考虑不可变的问题,极大的简化了更新状态的逻辑。我们一般用 createAction
配合使用
1 | // before |
现在我们的代码变得更加简洁,几乎没有了模板代码,非常的优雅!
除了上面我演示的这种方式以外,RTK 还支持使用 Builder Callback
这种方式,大家要是感兴趣可以自行查看文档:https://redux-toolkit.js.org/api/createReducer#usage-with-the-builder-callback-notation
好了,关于 RTK 的话题我们就介绍到这里了,更多有趣的内容请关注我的专栏!