redux-toolkit介绍
徐徐 抱歉选手

在使用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
2
3
4
5
6
7
8
// slice file中定义在createSlice的reducer field里的reducer function
reactionAdded(state, action) {const { postId, reaction } = action.payload}

// UI component
<button key={name}
type="button"
className="muted-button reaction-button"
onClick={() => dispatch(reactionAdded({postId: post.id, reaction: name}))}>{emoji} {post.reactions[name]}</button>

数据都是以对象的形式,按照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
2
3
4
5
6
7
8
9
10
11
12
// reducer中createSlice的reducers field
todoColorSelected: {
reducer(state, action) {
const { color, todoId } = action.payload
state.entities[todoId].color = color
},
prepare(todoId, color) {
return {
payload: { todoId, color }
}
}
},

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 a payload field (or, optionally, meta and error 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
2
3
createAsyncThunk.pending: feature/createAsyncThunk/pending
createAsyncThunk.fulfilled: feature/createAsyncThunk/fulfilled
createAsyncThunk.rejected: feature/createAsyncThunk/rejected

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 state
  • upsertOne / upsertMany: add new items or update existing ones
  • updateOne / updateMany: update existing items by supplying partial values
  • removeOne / removeMany: remove items based on IDs
  • setAll: 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
2
3
4
5
// todos slice in whole redux store
export const {
selectAll: selectTodos,
selectById: selectTodoById
} = todosAdapter.getSelectors(state => state.todos)// this slice
  • 本文标题:redux-toolkit介绍
  • 本文作者:徐徐
  • 创建时间:2020-12-10 20:29:48
  • 本文链接:https://machacroissant.github.io/2020/12/10/redux-toolkit/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论