目录
[一、Redux 介绍](#一、Redux 介绍)
[为什么要使用 Redux?](#为什么要使用 Redux?)
[二、Redux 快速体验](#二、Redux 快速体验)
[1. 实现计数器](#1. 实现计数器)
[2. Redux 数据流架构](#2. Redux 数据流架构)
[三、Redux 与 React - 环境准备](#三、Redux 与 React - 环境准备)
[1. 配套工具](#1. 配套工具)
[2. 配置基础环境](#2. 配置基础环境)
[3. store 目录结构设计](#3. store 目录结构设计)
[四、Redux 与 React - 实现 counter](#四、Redux 与 React - 实现 counter)
[1. 整体路径熟悉](#1. 整体路径熟悉)
[2. 使用 React Toolkit 创建 counterStore](#2. 使用 React Toolkit 创建 counterStore)
[3. 为 React 注入 store](#3. 为 React 注入 store)
[4. React 组件使用 store 中的数据](#4. React 组件使用 store 中的数据)
[5. React 组件修改 store 中的数据](#5. React 组件修改 store 中的数据)
[五、Redux 与 React - 提交 action 传参](#五、Redux 与 React - 提交 action 传参)
[六、Redux 与 React - 异步 action 处理](#六、Redux 与 React - 异步 action 处理)
[七、Redux 调试 - devtools](#七、Redux 调试 - devtools)
[1. 案例演示](#1. 案例演示)
[2. 分类和商品列表渲染](#2. 分类和商品列表渲染)
[3. 点击分类激活交互实现](#3. 点击分类激活交互实现)
[4. 商品列表切换显示](#4. 商品列表切换显示)
[5. 添加购物车实现](#5. 添加购物车实现)
[6. 统计区域实现](#6. 统计区域实现)
[7. 购物车列表功能实现](#7. 购物车列表功能实现)
[8. 控制购物车显示和隐藏](#8. 控制购物车显示和隐藏)
[Redux 核心概念回顾](#Redux 核心概念回顾)
[Redux Toolkit 优势](#Redux Toolkit 优势)
[什么时候使用 Redux](#什么时候使用 Redux)
Redux是一个独立于框架的集中状态管理工具,通过单向数据流管理应用状态。核心概念包括state(状态数据)、action(操作描述)和reducer(状态更新函数)。
使用Redux Toolkit可简化开发,提供createSlice创建模块化store,configureStore组合多个reducer。在React中结合react-redux使用,通过Provider注入store,useSelector获取状态,useDispatch提交action。
支持异步操作处理,通过中间件实现。Redux适用于中大型应用的状态共享,提供调试工具和清晰的架构,但简单场景可能更适合使用React原生状态管理。
购物车案例展示了Redux在实战中的应用,包括状态管理、异步请求和UI交互。
一、Redux 介绍
Redux 是 React 最常用的集中状态管理工具,类似于 Vue 中的 Pinia(Vuex),可以独立于框架运行。它通过集中管理的方式管理应用的状态,解决了复杂应用中组件间状态共享的问题。

为什么要使用 Redux?
-
独立于组件:无视组件之间的层级关系,简化组件间通信问题
-
单项数据流清晰:数据流单向流动,易于定位 bug
-
调试工具配套良好:提供了强大的调试工具,方便开发和调试
二、Redux 快速体验
1. 实现计数器
我们来实现一个不绑定任何框架、不使用任何构建工具的纯 Redux 计数器。

使用步骤:
-
定义一个 reducer 函数(根据当前想要做的修改返回一个新的状态)
-
使用 createStore 方法传入 reducer 函数生成一个 store 实例对象
-
使用 store 实例的 subscribe 方法订阅数据的变化(数据一旦变化,可以得到通知)
-
使用 store 实例的 dispatch 方法提交 action 对象触发数据变化(告诉 reducer 你想怎么改数据)
-
使用 store 实例的 getState 方法获取最新的状态数据更新到视图中
代码实现:
html
<button id="decrement">-</button>
<span id="count">0</span>
<button id="increment">+</button>
<script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
<script>
// 定义reducer函数
// 内部主要的工作是根据不同的action 返回不同的state
function counterReducer (state = { count: 0 }, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 }
case 'DECREMENT':
return { count: state.count - 1 }
default:
return state
}
}
// 使用reducer函数生成store实例
const store = Redux.createStore(counterReducer)
// 订阅数据变化
store.subscribe(() => {
console.log(store.getState())
document.getElementById('count').innerText = store.getState().count
})
// 增
const inBtn = document.getElementById('increment')
inBtn.addEventListener('click', () => {
store.dispatch({
type: 'INCREMENT'
})
})
// 减
const dBtn = document.getElementById('decrement')
dBtn.addEventListener('click', () => {
store.dispatch({
type: 'DECREMENT'
})
})
</script>
2. Redux 数据流架构
Redux 的难点是理解它对于数据修改的规则,下图动态展示了在整个数据的修改中,数据的流向。

为了职责清晰,Redux 代码被分为三个核心的概念,我们学 Redux,其实就是学这三个核心概念之间的配合:
-
state:一个对象,存放着我们管理的数据
-
action:一个对象,用来描述你想怎么改数据
-
reducer:一个函数,根据 action 的描述更新 state
三、Redux 与 React - 环境准备
Redux 虽然是一个框架无关可以独立运行的插件,但是社区通常还是把它与 React 绑定在一起使用。我们以一个计数器案例体验一下 Redux + React 的基础使用。
1. 配套工具
在 React 中使用 Redux,官方要求安装两个其他插件:
-
Redux Toolkit(RTK):官方推荐编写 Redux 逻辑的方式,是一套工具的集合,简化书写方式
-
react-redux:用来链接 Redux 和 React 组件的中间件
2. 配置基础环境
1.使用 CRA 快速创建 React 项目
bash
npx create-react-app react-redux
2.安装配套工具
bash
// 若已经创建好react项目,可直接使用此命令,下载redux等工具
npm i @reduxjs/toolkit react-redux
3.启动项目
bash
npm run start
3. store 目录结构设计

-
通常集中状态管理的部分都会单独创建一个单独的
store目录 -
应用通常会有很多个子 store 模块,所以创建一个
modules目录,在内部编写业务分类的子 store -
store 中的入口文件 index.js 的作用是组合 modules 中所有的子模块,并导出 store
四、Redux 与 React - 实现 counter
1. 整体路径熟悉

2. 使用 React Toolkit 创建 counterStore
html
import { createSlice } from '@reduxjs/toolkit'
const counterStore = createSlice({
// 模块名称独一无二
name: 'counter',
// 初始数据
initialState: {
count: 1
},
// 修改数据的同步方法
reducers: {
increment (state) {
state.count++
},
decrement(state){
state.count--
}
}
})
// 结构出actionCreater
const { increment, decrement } = counterStore.actions
// 获取reducer函数
const counterReducer = counterStore.reducer
// 导出
export { increment, decrement }
export default counterReducer
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './modules/counterStore'
export default configureStore({
reducer: {
// 注册子模块
counter: counterReducer
}
})
3. 为 React 注入 store
react-redux 负责把 Redux 和 React 链接起来,内置 Provider 组件通过 store 参数把创建好的 store 实例注入到应用中,链接正式建立。
html
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
// 导入store
import store from './store'
// 导入store提供组件Provider
import { Provider } from 'react-redux'
ReactDOM.createRoot(document.getElementById('root')).render(
// 提供store数据
<Provider store={store}>
<App />
</Provider>
)
4. React 组件使用 store 中的数据
在 React 组件中使用 store 中的数据,需要用到一个钩子函数 - useSelector,它的作用是把 store 中的数据映射到组件中。

5. React 组件修改 store 中的数据
React 组件中修改 store 中的数据需要借助另外一个 hook 函数 - useDispatch,它的作用是生成提交 action 对象的 dispatch 函数。

五、Redux 与 React - 提交 action 传参
需求: 组件中有两个按钮 add to 10 和 add to 20 可以直接把 count 值修改到对应的数字,目标 count 值是在组件中传递过去的,需要在提交 action 的时候传递参数。

实现方式: 在 reducers 的同步修改方法中添加 action 对象参数,在调用 actionCreater 的时候传递参数,参数会被传递到 action 对象 payload 属性上。

六、Redux 与 React - 异步 action 处理
需求理解:

实现步骤:
-
创建 store 的写法保持不变,配置好同步修改状态的方法
-
单独封装一个函数,在函数内部 return 一个新函数,在新函数中2.1 封装异步请求获取数据2.2 调用同步 actionCreater 传入异步数据生成一个 action 对象,并使用 dispatch 提交
-
组件中 dispatch 的写法保持不变
代码实现:
html
import { createSlice } from '@reduxjs/toolkit'
import axios from 'axios'
const channelStore = createSlice({
name: 'channel',
initialState: {
channelList: []
},
reducers: {
setChannelList (state, action) {
state.channelList = action.payload
}
}
})
// 创建异步
const { setChannelList } = channelStore.actions
const url = 'https://api.example.com/v1_0/channels'
// 封装一个函数 在函数中return一个新函数 在新函数中封装异步
// 得到数据之后通过dispatch函数 触发修改
const fetchChannelList = () => {
return async (dispatch) => {
const res = await axios.get(url)
dispatch(setChannelList(res.data.data.channels))
}
}
export { fetchChannelList }
const channelReducer = channelStore.reducer
export default channelReducer
import { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { fetchChannelList } from './store/channelStore'
function App () {
// 使用数据
const { channelList } = useSelector(state => state.channel)
useEffect(() => {
dispatch(fetchChannelList())
}, [dispatch])
return (
<div className="App">
<ul>
{channelList.map(task => <li key={task.id}>{task.name}</li>)}
</ul>
</div>
)
}
export default App
七、Redux 调试 - devtools
Redux 官方提供了针对 Redux 的调试工具,支持实时 state 信息展示,action 提交信息查看等。

八、实战案例:购物车应用
1. 案例演示

基本开发思路: 使用 RTK(Redux Toolkit)来管理应用状态,组件负责数据渲染和 dispatch action。
2. 分类和商品列表渲染

1- 编写 store 逻辑:
html
import { createSlice } from "@reduxjs/toolkit"
import axios from "axios"
const foodsStore = createSlice({
name: 'foods',
initialState: {
// 商品列表
foodsList: []
},
reducers: {
// 更改商品列表
setFoodsList (state, action) {
state.foodsList = action.payload
}
}
})
// 异步获取部分
const { setFoodsList } = foodsStore.actions
const fetchFoodsList = () => {
return async (dispatch) => {
// 编写异步逻辑
const res = await axios.get('http://localhost:3004/takeaway')
// 调用dispatch函数提交action
dispatch(setFoodsList(res.data))
}
}
export { fetchFoodsList }
const reducer = foodsStore.reducer
export default reducer
2- 组件使用 store 数据:
html
import { useDispatch, useSelector } from 'react-redux'
import { fetchFoodsList } from './store/modules/takeaway'
import { useEffect } from 'react'
const App = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(fetchFoodsList())
}, [dispatch])
const { foodsList } = useSelector(state => state.foods)
return (
<div className="home">
<NavBar />
<div className="content-wrap">
<div className="content">
<Menu />
<div className="list-content">
<div className="goods-list">
{foodsList.map(item => {
return (
<FoodsCategory
key={item.tag}
name={item.name}
foods={item.foods}
/>
)
})}
</div>
</div>
</div>
</div>
<Cart />
</div>
)
}
export default App
3. 点击分类激活交互实现

1- 编写 store 逻辑:
html
import { createSlice } from "@reduxjs/toolkit"
const foodsStore = createSlice({
name: 'foods',
initialState: {
// 菜单激活下标值
activeIndex: 0
},
reducers: {
// 更改activeIndex
changeActiveIndex (state, action) {
state.activeIndex = action.payload
}
}
})
// 导出
const { changeActiveIndex } = foodsStore.actions
export { changeActiveIndex }
const reducer = foodsStore.reducer
export default reducer
2- 编写组件逻辑:
html
const Menu = () => {
const { foodsList, activeIndex } = useSelector(state => state.foods)
const dispatch = useDispatch()
const menus = foodsList.map(item => ({ tag: item.tag, name: item.name }))
return (
<nav className="list-menu">
{menus.map((item, index) => {
return (
<div
onClick={() => dispatch(changeActiveIndex(index))}
key={item.tag}
className={classNames(
'list-menu-item',
activeIndex === index && 'active'
)}
>
{item.name}
</div>
)
})}
</nav>
)
}
4. 商品列表切换显示

html
<div className="list-content">
<div className="goods-list">
{foodsList.map((item, index) => {
return (
activeIndex === index && <FoodsCategory
key={item.tag}
name={item.name}
foods={item.foods}
/>
)
})}
</div>
</div>
5. 添加购物车实现

1- 编写 store 逻辑:
html
import { createSlice } from "@reduxjs/toolkit"
const foodsStore = createSlice({
name: 'foods',
reducers: {
// 添加购物车
addCart (state, action) {
// 是否添加过?以action.payload.id去cartList中匹配 匹配到了 添加过
const item = state.cartList.find(item => item.id === action.payload.id)
if (item) {
item.count++
} else {
state.cartList.push(action.payload)
}
}
}
})
// 导出actionCreater
const { addCart } = foodsStore.actions
export { addCart }
const reducer = foodsStore.reducer
export default reducer
2- 编写组件逻辑:
html
<div className="goods-count">
<span
className="plus"
onClick={() => dispatch(addCart({
id,
picture,
name,
unit,
description,
food_tag_list,
month_saled,
like_ratio_desc,
price,
tag,
count
}))}></span>
</div>
6. 统计区域实现

实现思路:
-
基于 store 中的 cartList 的 length 渲染数量
-
基于 store 中的 cartList 累加 price * count
-
购物车 cartList 的 length 不为零则高亮
html
// 计算总价
const totalPrice = cartList.reduce((a, c) => a + c.price * c.count, 0)
{/* fill 添加fill类名购物车高亮*/}
{/* 购物车数量 */}
<div onClick={onShow} className={classNames('icon', cartList.length > 0 && 'fill')}>
{cartList.length > 0 && <div className="cartCornerMark">{cartList.length}</div>}
</div>
7. 购物车列表功能实现

1- 控制列表渲染:
html
const Cart = () => {
const { cartList } = useSelector(state => state.foods)
return (
<div className="cartContainer">
<div className={classNames('cartPanel', 'visible')}>
<div className="scrollArea">
{cartList.map(item => {
return (
<div className="cartItem" key={item.id}>
<img className="shopPic" src={item.picture} alt="" />
<div className="main">
<div className="skuInfo">
<div className="name">{item.name}</div>
</div>
<div className="payableAmount">
<span className="yuan">¥</span>
<span className="price">{item.price}</span>
</div>
</div>
<div className="skuBtnWrapper btnGroup">
<Count
count={item.count}
/>
</div>
</div>
)
})}
</div>
</div>
</div>
)
}
export default Cart
2- 购物车增减逻辑实现:
html
// count增
increCount (state, action) {
// 关键点:找到当前要修改谁的count id
const item = state.cartList.find(item => item.id === action.payload.id)
item.count++
},
// count减
decreCount (state, action) {
// 关键点:找到当前要修改谁的count id
const item = state.cartList.find(item => item.id === action.payload.id)
if (item.count === 0) {
return
}
item.count--
}
<div className="skuBtnWrapper btnGroup">
<Count
count={item.count}
onPlus={() => dispatch(increCount({ id: item.id }))}
onMinus={() => dispatch(decreCount({ id: item.id }))}
/>
</div>
3- 清空购物车实现:
html
// 清除购物车
clearCart (state) {
state.cartList = []
}
<div className="header">
<span className="text">购物车</span>
<span
className="clearCart"
onClick={() => dispatch(clearCart())}>
清空购物车
</span>
</div>
8. 控制购物车显示和隐藏

html
// 控制购物车打开关闭的状态
const [visible, setVisible] = useState(false)
const onShow = () => {
if (cartList.length > 0) {
setVisible(true)
}
}
{/* 遮罩层 添加visible类名可以显示出来 */}
<div
className={
classNames('cartOverlay', visible && 'visible')
}
onClick={() => setVisible(false)}
/>
九、总结
Redux 核心概念回顾
-
State:应用的状态数据
-
Action:描述要执行的操作的对象,包含 type 和可选的 payload
-
Reducer:纯函数,根据当前 state 和 action 返回新的 state
-
Store:将 state、action、reducer 结合在一起的对象
Redux Toolkit 优势
-
简化代码:自动处理不可变更新
-
减少样板代码:createSlice 和 configureStore 大大简化了代码
-
内置常用功能:包含了 Immer、Redux-Thunk 等常用库
-
类型安全:对 TypeScript 支持良好
最佳实践
-
使用 Redux Toolkit 而不是原生 Redux
-
按功能模块组织 store(feature-based slicing)
-
保持 reducer 纯净,不要在里面执行异步操作
-
使用 createAsyncThunk 处理异步逻辑
-
合理使用 useSelector 和 useDispatch
-
利用 Redux DevTools 进行调试
什么时候使用 Redux
-
应用有大量的状态需要在多个组件间共享
-
状态需要频繁更新
-
状态更新逻辑复杂
-
需要在组件树的不同层级间传递状态
Redux 是一个强大的状态管理工具,但也不是所有项目都需要。对于简单的应用,React 的 useState 和 useContext 可能就足够了。但对于中大型应用,Redux 可以帮助你更好地管理状态,提高代码的可维护性。
