太棒了!👏 你已经掌握了 React 的核心能力,现在是时候构建大型应用所需的状态管理架构了。
—— 用 Context + useReducer 构建全局状态
🎯 目标:解决“跨组件共享状态”问题,如:
- 用户登录信息
- 购物车
- 主题设置
- 多步骤表单数据
当你发现要一层层传递 props 或频繁使用回调时,就是时候升级状态管理了!
一、为什么需要更强大的状态管理?⚠️
❌ 常见痛点:
| 问题 | 描述 |
|---|---|
| Prop Drilling | 状态要穿过多个中间组件才能到达深层子组件 |
| 状态分散 | 相关逻辑散落在多个组件中,难以维护 |
| 兄弟通信复杂 | 必须通过父组件中转,代码臃肿 |
✅ 解决方案演进路径:
Props → Context → Context + useReducer → Redux / Zustand
我们先掌握 Context + useReducer —— 它是理解 Redux 的最佳跳板。
二、useReducer:管理复杂状态的 Hook 🧠
📌 类比:
useState:适合简单值(数字、字符串、布尔)useReducer:适合复杂对象或有多种变化逻辑的状态
✅ 基本语法:
const [state, dispatch] = useReducer(reducer, initialState);
reducer:一个函数,接收(state, action)并返回新 statedispatch:派发动作的方式,如dispatch({ type: 'ADD_ITEM' })
💡 示例:购物车状态管理
1. 定义初始状态和 reducer
// cartStore.js
const initialState = {
items: [],
total: 0,
};
function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
return {
...state,
items: [...state.items, action.payload],
total: state.total + action.payload.price,
};
case 'REMOVE_ITEM':
const item = state.items.find(item => item.id === action.payload);
return {
...state,
items: state.items.filter(item => item.id !== action.payload),
total: state.total - (item?.price || 0),
};
case 'CLEAR_CART':
return initialState;
default:
return state;
}
}
2. 使用 useReducer
import { useReducer } from 'react';
import { cartReducer, initialState } from './cartStore';
function ShoppingCart() {
const [state, dispatch] = useReducer(cartReducer, initialState);
return (
<div>
<p>商品数量:{state.items.length}</p>
<p>总价:¥{state.total}</p>
<button onClick={() =>
dispatch({
type: 'ADD_ITEM',
payload: { id: Date.now(), name: '商品', price: 99 }
})
}>
添加商品
</button>
<button onClick={() => dispatch({ type: 'CLEAR_CART' })}>
清空购物车
</button>
</div>
);
}
✅ 关键优势:
- 所有状态变更集中在
reducer中 - 易于调试、测试、追踪变化
- 支持“时间旅行”思想(Redux 核心理念)
三、结合 Context:实现全局状态共享 🌐
现在让多个组件都能访问和修改这个购物车状态。
✅ 步骤 1:创建 Context
// CartContext.js
import { createContext } from 'react';
const CartContext = createContext();
export default CartContext;
✅ 步骤 2:创建 Provider 组件
// CartProvider.jsx
import { useReducer } from 'react';
import CartContext from './CartContext';
import { cartReducer, initialState } from './cartStore';
export function CartProvider({ children }) {
const [state, dispatch] = useReducer(cartReducer, initialState);
return (
<CartContext.Provider value={{ state, dispatch }}>
{children}
</CartContext.Provider>
);
}
✅ 步骤 3:在顶层包裹应用
// App.js
import { CartProvider } from './CartProvider';
function App() {
return (
<CartProvider>
<Header />
<Main />
<Sidebar />
</CartProvider>
);
}
✅ 步骤 4:任意组件使用状态
// Header.js
import { useContext } from 'react';
import CartContext from './CartContext';
function Header() {
const { state } = useContext(CartContext);
return (
<header>
<h1>我的商城</h1>
<span>购物车:{state.items.length} 件</span>
</header>
);
}
// ProductItem.js
import { useContext } from 'react';
import CartContext from './CartContext';
function ProductItem({ product }) {
const { dispatch } = useContext(CartContext);
return (
<div>
<h3>{product.name}</h3>
<p>¥{product.price}</p>
<button onClick={() =>
dispatch({
type: 'ADD_ITEM',
payload: product
})
}>
加入购物车
</button>
</div>
);
}
四、优化建议与最佳实践 ✅
1. 拆分 reducer(可选)
当逻辑变多时,可用 combineReducers 模式:
const rootReducer = combineReducers({
cart: cartReducer,
user: userReducer,
ui: uiReducer,
});
2. 封装 dispatch 逻辑
避免在组件中写复杂的 action:
// utils/cartActions.js
export const addToCart = (item) => ({
type: 'ADD_ITEM',
payload: item,
});
export const removeFromCart = (id) => ({
type: 'REMOVE_ITEM',
payload: id,
});
然后在组件中:
dispatch(addToCart(product));
3. 性能优化
- 使用
React.memo包裹不常变的组件 - 避免在
value中传太多数据 - 可考虑将
dispatch和state分开提供 Context(高级)
五、与 Redux 对比 🆚
| 特性 | Context + useReducer | Redux |
|---|---|---|
| 学习成本 | 低(原生支持) | 中高 |
| 调试工具 | 无内置 DevTools | 强大的 Redux DevTools |
| 中间件支持 | 需手动实现 | 支持 thunk/saga 等 |
| 社区生态 | 小而精 | 庞大成熟 |
| 适用场景 | 中小型项目 | 大型复杂应用 |
✅ 建议路径:
- 初级 → useState + props
- 中级 → Context + useReducer
- 高级 → Redux Toolkit 或 Zustand
六、实战练习 🏋️♀️
✅ 练习 1:用户登录状态管理
目标:创建一个全局登录状态系统
- 定义
authReducer:
-
- 初始状态:
{ user: null, isLoggedIn: false } - Action:
LOGIN,LOGOUT
- 初始状态:
- 创建
AuthContext和AuthProvider - 在
LoginButton组件中调用dispatch({ type: 'LOGIN', payload: user }) - 在
Profile组件中显示用户名或“未登录”
✅ 练习 2:主题切换器(进阶)
- 状态包含:
theme: 'light' | 'dark' - Action:
TOGGLE_THEME - 在
App根节点根据state.theme添加className - 按钮点击触发切换
💡 提示:结合 useEffect 把主题保存到 localStorage
✅ 总结:状态管理进阶要点
| 概念 | 要点 |
|---|---|
useReducer | 适合复杂状态,集中管理变化逻辑 |
Context | 提供全局可访问的数据通道 |
| 组合使用 | 实现轻量级全局状态管理 |
| Action 对象 | { type, payload } 结构清晰 |
| 单向数据流 | State → View → Dispatch → Reducer → New State |
| 为 Redux 铺路 | 理解这套模式后,学 Redux 会非常轻松 |
🎯 下一步预告:
你已经具备构建中大型应用的能力!接下来我们要进入现代化开发流程:
➡️ React + TypeScript:类型安全开发
—— 让你的代码更健壮、智能提示更强、团队协作更高效
是否继续?我将带你进入 第七课:TypeScript 与 React 的完美结合。