一、安装RTK相关包和开发工具
// 安装 @reduxjs/toolkit 和 react-redux
npm install @reduxjs/toolkit react-redux
@reduxjs/toolkit 封装了最开始redux的一些逻辑而 react-redux 可以使用Provider和connect来将仓库里的state和dispatch映射到组件props中
二、基本开发流程
- 创建store文件夹
- 创建index.js作为入口文件,主要作用是通过
import { configureStore } from "@reduxjs/toolkit"来创建并导出一个store
import { configureStore } from "@reduxjs/toolkit"
// 导入counter模块下的reducer函数
import counterReducer from "./features/counter"
const store = configureStore({
reducer: {
// 每个reducer都可以理解成一个子仓库
counter: counterReducer
}
})
export default store
- 下面完成counter模块的reducer的实现,就是实现一个计数器的功能,主要依赖@reduxjs/toolkit里的createSlice(),使用createSlice可以返回一个Slice对象,这个Slice对象里面有当前store模块的reducer和actions。怎么创建一个Slice呢?我们只需要传入一个配置对象
import { createSlice } from "@reduxjs/toolkit"
const counterSlice = createSlice({
name: "counter", // store的名字
initialState: {
counter: 100
},
reducers: {
addNumber(state, action) {
// 这里可以拿到state直接赋值,在最开始的写法中我们需要通过{ ...state, ...newObj }返回一个新的对象
state.counter = state.counter + action.payload
},
subNumber(state, { payload }) {
state.counter = state.counter - payload
}
}
})
// 创建好store之后,我们仍需要将Slice对象的actions和reducer导出
// 这里的addNumber和subNumber其实就是一个actionCreator(),调用即可获得一个action对象,
// 例如 addNumber(12)的返回值是{ type: 'addNumber', payload: 12}这样一个action对象
export const { addNumber, subNumber } = counterSlice.actions
export default counterSlice.reducer
需要注意的是,在@reduxjs/toolkit中我们可以直接对state进行赋值,因为已经帮我们封装好了,官网有解释到:
// Redux Toolkit 允许在 reducers 中编写 "mutating" 逻辑。
// 它实际上并没有改变 state,因为使用的是 Immer 库,检测到“草稿 state”的变化并产生一个全新的
// 基于这些更改的不可变的 state。
- 跟Vuex和Pinia一样,我们需要再main.js(index.js)中引入导出的store,并且通过Provider注册store
import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import App from './App'
import store from './store'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
// <React.StrictMode>
<Provider store={store}>
<App />
</Provider>
// </React.StrictMode>
)
- 现在我们就可以在Home和About两个子组件中使用counterStore中的数据,在About组件中我们只使用store中的数据不修改,在Home组件中我们既读取也修改。
// About.jsx
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
export class About extends PureComponent {
render() {
const { counter } = this.props
return (
<div>
<h2>About Counter: {counter}</h2>
</div>
)
}
}
const mapStateToProps = (state) => ({
// state可以看成store,里面有counter这个模块,counter里面有counter这个数据
counter: state.counter.counter
})
export default connect(mapStateToProps)(About)
// Home.jsx
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import { addNumber } from '../store/features/counter'
export class Home extends PureComponent {
render() {
const { counter, increament } = this.props
return (
<div>
<h2>Home Counter: {counter}</h2>
<button onClick={(e) => increament(5)}>+5</button>
<button onClick={(e) => increament(8)}>+8</button>
<button onClick={(e) => increament(18)}>+18</button>
</div>
)
}
}
const mapStateToProps = (state) => ({
counter: state.counter.counter
})
const mapDispatchToProps = (dispatch) => ({
increament(num) {
dispatch(addNumber(num))
}
})
export default connect(mapStateToProps, mapDispatchToProps)(Home)
这样一个简单的计数器功能就实现了
三、异步操作
下面,我们对上面的案例进行升级,使他更像开发中的情况(开发中我们肯定会有多个Slice),我们新建一个模块叫做home,home中存有轮播图数组和recommends数组,并在home中发送异步请求
import homeReducer from './features/home'
const store = configureStore({
reducer: {
counter: counterReducer,
home: homeReducer // index.js中别忘了引入home模块
}
})
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import axios from 'axios'
const homeSlice = createSlice({
name: 'home',
initialState: {
banners: [],
recommends: []
},
reducers: {
changeBanners(state, { payload }) {
state.banners = payload
},
changeRecommends(state, { payload }) {
state.recommends = payload
}
},
// 当Slice中需要发送异步请求时,需要配置这个函数
extraReducers: (builder) => {
builder
.addCase(homeMultidataAction.pending, (state, action) => {
console.log('fetchHomeMultidataAction pending')
})
.addCase(homeMultidataAction.fulfilled, (state, { payload }) => {
state.banners = payload.data.banner.list
state.recommends = payload.data.recommend.list
})
}
})
export const { changeBanners, changeRecommends } = homeSlice.actions
// thunk函数允许执行异步逻辑, 通常用于发出异步请求。
// createAsyncThunk 创建一个异步action,方法触发的时候会有三种状态:
// pending(进行中)、fulfilled(成功)、rejected(失败)
export const homeMultidataAction = createAsyncThunk('fetch/homemultidata', async () => {
// 1.发送网络请求, 获取数据
const res = await axios.get('http://123.207.32.32:8000/home/multidata')
// 2.返回结果, 那么action状态会变成fulfilled状态,这里返回的结果会变成action的payload
return res.data
})
export default homeSlice.reducer
现在我们在Home组件中发起的componentDidMount函数中发起请求,并且获取仓库中的异步数据进行展示: Home.jsx
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import { addNumber } from '../store/features/counter'
import { homeMultidataAction } from '../store/features/home'
export class Home extends PureComponent {
componentDidMount() {
this.props.getHomeMultidata()
}
render() {
const { counter, increament, banners, recommends } = this.props
return (
<div>
<h2>Home Counter: {counter}</h2>
<button onClick={(e) => increament(5)}>+5</button>
<button onClick={(e) => increament(8)}>+8</button>
<button onClick={(e) => increament(18)}>+18</button>
展示数据
<div className='banner'>
<h2>轮播图展示</h2>
<ul>
{banners.map((item, index) => {
return <li key={index}>{item.title}</li>
})}
</ul>
</div>
<div className='recommend'>
<h2>推荐的展示</h2>
<ul>
{recommends.map((item, index) => {
return <li key={index}>{item.title}</li>
})}
</ul>
</div>
</div>
)
}
}
const mapStateToProps = (state) => ({
counter: state.counter.counter
})
const mapDispatchToProps = (dispatch) => ({
increament(num) {
dispatch(addNumber(num))
},
getHomeMultidata() {
dispatch(homeMultidataAction())
}
})
export default connect(mapStateToProps, mapDispatchToProps)(Home)