在使用redux toolkit之前需要在项目中安装依赖包。
1 | npm install @reduxjs/toolkit |
由于redux toolkit本身已经是依赖于redux/redux-thunk/reselect这些依赖包的抽象集合,所以可以不再使用这些基础依赖包。
1 | npm uninstall redux redux-thunk reselect |
这些基础依赖包提供的API都能在redux-toolkit中找到。
Store Setup
手写store基础配置
在redux fundamentals与redux essentials的教程中都涉及了最底层的创建redux store的流程。
为了创建一个redux store,需要经历以下几个步骤:
- import各个slices中的reducers到rootReducer文件中,combine形成rootReducer
- 将rootReducer文件中的rootReducer添加到store file中
- 在store file中import用于配置thunk middleware/applyMiddleware等方法
- 将多个配置方法使用composeWithDevTools组合起来形成唯一的一个composed store enhancer
- 调用createStore并传入root reducer和composed store enhancer
使用configureStore
Redux Toolkit提供了configureStore API简化以上五个步骤。
configureStore为我们直接提供了以下功能:
- 默认combine slice reducers为rootReducer
- 跳过rootReducer直接创建redux store
- 自动添加thunk middleware
- 自动添加其他一些能够检查mutating updates的middleware,如果我们在slice内部的reducer中进行了mutable updates就会报错
- 自动将store连接到Redux DevTools Extension
Writing Slices
redux store要求对state的变更必须是immutable的,因此在书写slices的时候,需要一遍一遍地先copy原来的obj,再修改原来的obj,再整体return obj。
而且reducer的书写是以switch/case的形式书写的,每一次都需要传入action type进行匹配。
Redux Toolkit提供了createSlice API简化reducer logic and actions。
createSlice优点
createSlice为我们直接提供了以下功能:
- 直接在reducers对象内部以函数的形式书写case reducers,不需要使用switch/case
We write case reducer functions inside the
reducers
object, and give them readable names
- 由于Immer Library的配置,可以在createSlice内部写”mutable update logic”
createSlice
allows us to safely “mutate” our state!
- action creator会因为reducer function的定义而自动生成
createSlice
will automatically generate action creators that correspond to each case reducer function we provide可以通过访问slice.actions.reducerFunctionName来访问自动生成的actioncreator。
action creator的action type就是name filed + reducer field里的function name
使用createSlice
createSlice()参数接受一个含有三个主要fielde的对象:
- name field是自动生成的action creator的action type的前缀
- initialState field是reducer的初始状态
- reducers field是一个key为string,value为case reducer functions的函数。
每次一完成createSlice的定义后都有两行需要export的内容。
第一行是需要被import到redux store的reducer整体:export default todosSlice.reducer
。
第二行是需要被import到UI component的、自动生成的reducer.actions:export const { todoAdded, todoToggled } = todosSlice.actions
。
如何为reducer field中的reducer function传递更多的参数?在UI component中调用dispatch的时候传递给dispatch的action的参数是和slice中定义的action.payload是形式上是一致的。
1 | // slice file中定义在createSlice的reducer field里的reducer function |
数据都是以对象的形式,按照key和value的形式一一对应传递的。
如果遇到从UI component传入多个参数需要经过某些preparation logic(也就是UI传入的数据要做一些额外的变换)才能作为该action的payload传递到reducer function的情况下,该怎么办?
createSlice提供了为该reducer function添加一个prepare callback function field,用于把UI传过来的数据处理一下,在作为action.payload传递到reducer function中去运行。
在reducers field中将带有prepare callback的函数放在该reducer function名字的对象下,该对象有prepare field和reducer field。
1 | // reducer中createSlice的reducers field |
When we call the generated action creator, the
prepare
function will be called with whatever parameters were passed in. It should then create and return an object that has apayload
field (or, optionally,meta
anderror
fields)
Writing Thunks
Redux Toolkit has a createAsyncThunk
API that will generate these thunks for us.
createAsyncThunk定义与使用方法
createAsyncThunk接受两个参数:
- 第一个参数是string类型,作为自动生成的action的action type
- 第二个参数是返回Promise对象的payload creator callback function。通常用async/await语法书写。
1 | export const Thunk1 = createAsyncThunk('feature/Thunk1', async () => {}) |
createAsyncThunk内部会自动生成:三个action creators以及对应的action types,一个在(该thunk function)被调用的时候自动dispatch这些actions的thunk function。
1 | createAsyncThunk.pending: feature/createAsyncThunk/pending |
Thunk generated action in extraReducers
注意在我们的slice文件中thunk都是被书写在createSlice外部的,因此createSlice内部的状态是无法监听createSlice本身之外定义的action type的。
为了监听其他地方定义的action type,createSlice提供了extraReducers field。
createSlice
also accepts an extraReducers
option, where we can have the same slice reducer listen for other action types
该field就是一个callback function with a buidler parameter。当我们需要createSlice去监听其他的action type的时候,只要在callback function内部调用builder.addCase(actionCreator, caseReducer)
即可。
Normalizing State
normalizing state的好处就是可以用ID寻找任何一项数据本身,而不需要循环整个数组。与其用数组来储存数据本身,不如用对象想储存数据,以及数据相关的元数据,以及错误信息。
Redux Toolkit includes a createEntityAdapter
API that has prebuilt reducers for typical data update operations with normalized state.
createEntityAdapter的内置方法
Calling createEntityAdapter
gives us an “adapter” object that contains several premade reducer functions, including:
addOne
/addMany
: add new items to the stateupsertOne
/upsertMany
: add new items or update existing onesupdateOne
/updateMany
: update existing items by supplying partial valuesremoveOne
/removeMany
: remove items based on IDssetAll
: replace all existing items
以上都是和增删state value相关的操作。
getInitialState
: returns an object that looks like{ ids: [], entities: {} }
, for storing a normalized state of items along with an array of all item IDs
一般getInitialState可以作为createSlice的initialState field的参数传入。
getSelectors
: generates a standard set of selector functions。
getSelectors默认自动生成两个selector:返回所有items的数组的selectAll;以及返回一个item的selectById。但是由于这个通用的名称是不具有语境的,我们可以把它换名字变成当前slice语境下的函数。使用array destructing为selector重命名。
由于getSelector不知道应该在redux state tree中的那个分支找这个slice的数据,所以需要传递一个小小的selector来告诉getSelector,要找所有state中的当前slice的state。
1 | // todos slice in whole redux store |
- 本文标题:redux-toolkit介绍
- 本文作者:徐徐
- 创建时间:2020-12-10 20:29:48
- 本文链接:https://machacroissant.github.io/2020/12/10/redux-toolkit/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!