Redux Toolkit(RTK)是 Redux 官方推荐的编写 Redux 逻辑的方法。Redux 的编写逻辑过于繁琐,并且代码通常分拆在多个文件中,Redux Toolkit 就旨在解决 Redux 编写代码过于繁琐和分散的问题。
Redux Toolkit 其实就是对 Redux 做了一层封装。
安装:
npm install @reduxjs/toolkit
直接安装 Redux Toolkit 即可,不需要再单独安装 Redux。
但是如果想要更好地与 React 结合使用,还是需要安装react-redux
。
核心 API:
configureStore()
:
configureStore()
:创建 Store。用于替代 Redux 的 createStore()
方法。
接收一个对象作为参数,包含以下内容:
- reducer:对象类型。可以接收多个 reducer 函数。
原有的
combineReducers()
合并函数就不需要了。 - middleware:对象类型。可以接收多个中间件。
默认已经集成了
redux-thunk
和redux-devtool
中间件。 - devTools:布尔值类型。是否开启 devTools 工具,默认为 true。
// 引入 configureStore 方法
import {configureStore} from '@reduxjs/toolkit'
// 引入 reducer
import countReducer from './features/count'
// 创建 store
const store = configureStore({
// 将导出的 slice 中的 reducer 传入
reducer: {
count: countReducer,
},
})
// 导出 store
export default store
createSlice()
:
createSlice()
:创建一个 slice 片段。一个 slice 片段其实是 Action 和 Reducer 的集合。
createSlice()
接收一个对象作为参数,包含以下内容:
-
name:字符串类型。标记 slice。
作为
action.type
的前缀,在使用的时候默认使用name/actionName
,解决了 Action 中的 type 可能会同名的情况。
在 Redux DevTools 调试工具中可以看到。 -
initialState:对象类型。State 的初始值。
-
reducers:对象类型,相当于之前的 Reducer 函数。
对象中可以添加任意多个函数,每一个函数就相当于之前 Reducer 函数中的一个 case 语句。会接收到 State 和 Action 作为其参数(Action 有两个属性:type 和 payload,dispatch 派发 Action 时传入的参数就会作为 payload),直接修改 State 中对应的值即可。
Redux Toolkit 底层使用了 immerjs 库,因此可以直接使用赋值的方式改变 State,不再需要每一次都返回一个新的 State。
immerjs 底层是使用了 proxy 来对原状态进行了拦截,生成了草稿状态,在草稿状态上进行读写操作后,返回最终的状态。也就实现了既保证了原状态的不可变性,也保证了返回的是一个不同引用地址的新状态。 -
extraReducers:对象类型或者函数类型。可以让 slice 处理在别处定义的 Action, 包括由
createAsyncThunk()
或其他 slice 生成的 Action。
createSlice()
的返回值是一个对象,包含以下属性:
- reducer:当前 slice 片段中的所有 Reducer。
- actions:当前 slice 片段中的所有 Action。
// 引入 createSlice
import {createSlice} from "@reduxjs/toolkit";
// 创建 slice。每一个 slice 里都包含了 reducer 和 actions,可以实现模块化的封装
const slice = createSlice({
// 标记 slice,作为 action.type 的前缀
name: 'count',
// state 的初始值
initialState: {
count: 0,
},
// 相当于之前的 reducer 函数
reducers: {
// 一个函数就相当于之前的 reducer 函数中的一个 case 语句
// 相当于既定义了组件中 dispatch 使用的同步的 Action(函数名就相当于 Action 的类型),也定义了 Reducer 更新状态函数(函数体就相当于 Reducer 更新 State)
increaseCount(state, action) {
// 可以直接使用赋值的方式修改 State,不再需要每一次都返回一个新的 State
state.count += action.payload
},
decreaseCount(state, action) {
state.count -= action.payload
},
}
})
// 导出 slice 片段中的 reducer,以供创建 store 时使用
export default slice.reducer
// 导出 slice 片段中的 action,以供组件中 dispatch 时使用
export const {increaseCount, decreaseCount} = slice.actions
createAsyncThunk()
:
createAsyncThunk()
:创建一个异步 Action。
接收两个参数:第一个参数是一个表示类型的字符串,用于标识这个异步 Action,在 Redux DevTools 调试工具中可以看到;第二个参数是一个函数,可以在其中进行异步操作,返回的数据会被传入到 createSlice()
的 extraReducers 中函数的 action.payload
参数中。
createAsyncThunk()
方法触发的时候会有三种状态:pending(进行中)、fulfilled(成功)、rejected(失败),可以在 createSlice()
的 extraReducers 中监听结果。
// 引入 createAsyncThunk
import {createSlice, createAsyncThunk} from "@reduxjs/toolkit";
import axios from "axios";
// 通过 createAsyncThunk 创建一个异步的 Action 并导出
export const getAsyncCountAction = createAsyncThunk('getBannerAction', async () => {
// 执行异步操作
const res = await axios.get('http://123.207.32.32:8000/home/multidata')
// 返回的数据会被传入到 createSlice 的 extraReducers 中函数的 action.payload 参数中
return res.data.data.banner.list.length
})
const slice = createSlice({
name: 'count',
initialState: {
count: 0,
},
// extraReducers 可以让 slice 处理在别处定义的 Action,包括由 createAsyncThunk 创建的异步的 Action 或其他 slice 生成的 Action
extraReducers: {
[getAsyncCountAction.pending](state, action) {
console.log('getAsyncCountAction.pending')
},
// 监听 createAsyncThunk 创建的异步 Action 的结果,修改 State 中指定的数据
[getAsyncCountAction.fulfilled](state, action) {
console.log('getAsyncCountAction.fulfilled')
state.count = action.payload
},
[getAsyncCountAction.rejected](state, action) {
console.log('getAsyncCountAction.rejected')
},
},
// extraReducers 还有另外一种写法,可以是一个函数,接收一个 builder 作为参数,可以给 builder 添加 case 来监听异步操作的结果
extraReducers: builder => {
builder
.addCase(getAsyncCountAction.pending, (state, action) => {
console.log('getAsyncCountAction.pending')
})
.addCase(getAsyncCountAction.fulfilled, (state, action) => {
console.log('getAsyncCountAction.fulfilled')
state.count = action.payload
})
},
})
export default slice.reducer
export const {increaseCount, decreaseCount} = slice.actions
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
// 引入 Action
import {increaseCount, getAsyncCountAction} from '../store/features/count'
class Increase extends PureComponent {
componentDidMount() {
this.props.getInitialCount()
}
render() {
return (
<div>
<h1>{this.props.count}</h1>
</div>
)
}
}
const mapStateToProps = state => ({
// 获取 store 中的 count 数据
count: state.count.count,
})
const mapDispatchToProps = dispatch => ({
// 初始获取从服务器中请求到的 count 数据
getInitialCount: () => {
dispatch(getAsyncCountAction())
},
})
export default connect(mapStateToProps, mapDispatchToProps)(Increase)
示例:
// index.js
import ReactDOM from 'react-dom/client'
// 从 react-redux 中引入 Provider
import {Provider} from 'react-redux'
// 引入 store
import store from './store'
import App from './App'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
// 使用 <Provider> 包裹根组件,将 Store 作为属性赋值给 <Provider>
<Provider store={store}>
<App />
</Provider>
)
// APP.jsx
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
// 引入 Action
import {increaseCount, getAsyncCountAction} from './store/features/count'
class APP extends PureComponent {
componentDidMount() {
this.props.getInitialCount()
}
handleIncrease = () => {
this.props.handleCountIncrease()
}
render() {
return (
<div>
<h1>{this.props.count}</h1>
<button onClick={this.handleIncrease}>增加</button>
</div>
)
}
}
const mapStateToProps = state => ({
// 获取 store 中的 count 数据
count: state.count.count,
})
const mapDispatchToProps = dispatch => ({
// 初始获取从服务器中请求到的 count 数据
getInitialCount: () => {
dispatch(getAsyncCountAction())
},
handleCountIncrease: () => {
// 修改 store 中的 count 数据
dispatch(increaseCount(5))
}
})
export default connect(mapStateToProps, mapDispatchToProps)(APP)
// store/index.js
// 引入 configureStore 方法
import {configureStore} from '@reduxjs/toolkit'
// 引入 reducer
import countReducer from './features/count'
// 创建 store
const store = configureStore({
// 将导出的 slice 中的 reducer 传入
reducer: {
count: countReducer,
},
})
// 导出 store
export default store
// store/feature/count.js
// 引入 createSlice
import {createSlice, createAsyncThunk} from "@reduxjs/toolkit";
import axios from "axios";
// 通过 createAsyncThunk 创建一个异步的 Action 并导出
export const getAsyncCountAction = createAsyncThunk('getBannerAction', async () => {
// 执行异步操作
const res = await axios.get('http://123.207.32.32:8000/home/multidata')
// 返回的数据会被传入到 extraReducers 中函数的 action.payload 参数中
return res.data.data.banner.list.length
})
// 创建 slice。每一个 slice 里都包含了 reducer 和 actions,可以实现模块化的封装
const slice = createSlice({
// 标记 slice,作为 action.type 的前缀
name: 'count',
// state 的初始值
initialState: {
count: 0,
},
// 相当于之前的 reducer 函数
reducers: {
// 一个函数就相当于之前的 reducer 函数中的一个 case 语句
// 相当于既定义了组件中 dispatch 使用的同步 Action(函数名就相当于 Action 的类型),也定义了 Reducer 更新状态函数(函数体就相当于 Reducer 更新 State)
increaseCount(state, action) {
// 可以直接使用赋值的方式修改 State,不再需要每一次都返回一个新的 State
state.count += action.payload
},
decreaseCount(state, action) {
state.count -= action.payload
},
},
// extraReducers 可以让 slice 处理在别处定义的 Action,包括由 createAsyncThunk 创建的异步的 Action或其他 slice 生成的 Action
extraReducers: {
// 监听 createAsyncThunk 创建的异步 Action 的结果,修改 State 中指定的数据
[getAsyncCountAction.fulfilled](state, action) {
console.log('getAsyncCountAction.fulfilled')
state.count = action.payload
},
},
})
// 导出 slice 片段中的 reducer,以供创建 store 时使用
export default slice.reducer
// 导出 slice 片段中的 action,以供组件中 dispatch 时使用
export const {increaseCount, decreaseCount} = slice.actions