以下文章我们通过电商这个场景来详细聊聊 React 中的状态管理方案。
为什么需要状态管理?
你以前写前端,每个组件自己管自己的 state,用 useState 就够了。但电商场景里经常出现这样的情况:
购物车组件(显示数量)
↑
商品详情页组件(点"加入购物车")
↑
Header 组件(右上角购物车图标小红点)
这三个组件必须共享同一份「购物车数据」。如果各自管各自的,A 加了商品 B 不知道,这就炸了。
状态管理就是为了解决「多组件之间共享和同步数据」这个问题的。
一、useContext ------ React 自带的"全局变量"
本质
React 内置的发布-订阅 机制。用
createContext创建一个全局盒子,Provider 往盒子里放数据,任何子组件通过useContext取数据。
怎么工作的
jsx
// Step 1: 创建盒子
const CartContext = createContext()
// Step 2: 顶层用 Provider 包裹,value 就是共享的数据
function App() {
const [items, setItems] = useState([])
return (
<CartContext.Provider value={{ items, setItems }}>
<CartIcon /> {/* 小红点 */}
<ProductPage /> {/* 加购按钮 */}
</CartContext.Provider>
)
}
// Step 3: 任何子组件都能直接取
function CartIcon() {
const { items } = useContext(CartContext)
return <span>{items.length}</span>
}
最大的坑:性能问题
context value 一变,所有用了这个 context 的组件全部重新渲染,不管它们用到的字段是否变了。
items 从 [] 变成 [{id:1}]
↓
CartContext.Provider 的 value 变了
↓
CartIcon ??? ProductPage ??? ProductList ??? Footer ???
↓ ↓ ↓ ↓
全都重渲染! 重渲染! 重渲染! 重渲染!
所以 useContext 适合很小的项目,或者低频变化的数据(主题、语言)。电商购物车这种频繁增删的场景,用 useContext 会带来大量不必要的重渲染。
二、Redux ------ 开山鼻祖,生态最强
本质
单向数据流 + 全局 Store + 纯函数 Reducer。 状态不能直接改,必须通过 dispatch(action) → reducer 处理 → 生成新 state。
核心模型
Component Redux Store
│ │
│ dispatch({type:'cart/add'}) │
├─────────────────────────────>│
│ ├── Reducer 处理
│ │ ↓
│ │ 新 state
│ useSelector(s => s.cart) │
│<─────────────────────────────│
│ 只取需要的字段,只在这些字段变了才重渲染
关键设计思路
Redux 要求你通过 action 描述「发生了什么」,而不是直接说「状态变成什么」。这种设计来自 Flux 架构:
- action = 事件描述(
{type: "cart/add", payload: {id:1, name:"iPhone"}}) - reducer = 纯函数(接收旧 state + action,返回新 state,不产生副作用)
- dispatch = 触发更新的唯一入口
- selector = 从 store 里取一段数据
性能和样板代码的平衡
Redux 的问题------样板代码太多。加一个功能要写:action type 常量 → action creator → reducer case → 挂 slice → 组件里 dispatch。RTK(Redux Toolkit)大幅简化了这个,但样板还是比 Zustand 多。
不过好处是:可预测性强。每一个 state 变化都能通过 Redux DevTools 追踪到是谁发的 action 。
三、Zustand ------ 极简主义的 Redux 替代品
本质
一个基于 hooks、零样板代码的状态管理库。你定义一个 store,返回的就是一个 hook,直接在组件里用。
为什么它比 Redux 简洁
对比代码量就明白了------Redux 要:createSlice → configureStore → Provider → useSelector + useDispatch。Zustand 一步到位:
js
// 定义 store(一步到位)
const useCart = create((set) => ({
items: [],
total: 0,
addItem: (item) =>
set((state) => ({
items: [...state.items, item],
total: state.total + item.price
})),
clearCart: () => set({ items: [], total: 0 })
}))
// 组件里直接用
function CartIcon() {
const items = useCart((s) => s.items) // 只订阅 items,items 不变就不重渲染
const total = useCart((s) => s.total)
return <div>{items.length} 件, 共 {total} 元</div>
}
精髓:selector 机制
useCart(s => s.items) 这行是关键------通过选择器精确订阅某个字段 ,只有这个字段变了组件才重新渲染。性能上和 Redux 的 useSelector 一样好,但写法比 Redux 少了一半代码。
和 Redux 的本质区别
| Redux | Zustand | |
|---|---|---|
| 核心理念 | 通过 action 描述意图,reducer 纯函数处理 | 简单的 set 更新,更接近 setState |
| 心智模型 | 「事件溯源」式的 CQRS | 「增强版 useState」 |
| 样板代码 | 多(即使有 RTK) | 极少 |
| 学习曲线 | 中高(action/reducer/dispatch/selector/middleware...) | 低(一个 create 一把梭) |
四、MobX ------ 响应式编程,像 Vue 一样
本质
基于「可观察数据 + 自动追踪依赖」的响应式方案。 你定义的数据是
@observable的,谁用了这些数据,MobX 自动帮你追踪,数据变了自动通知它们更新。
和前面三个最大的不同
前面三个(Context、Redux、Zustand)都是 「手动订阅」模式------你得告诉框架"我要看哪些数据"。MobX 是「自动追踪」 ------你只管用,它自己知道谁依赖了谁。
js
class CartStore {
@observable items = []
@computed get itemCount() {
return this.items.length // 自动追踪依赖
}
@action
addItem(item) {
this.items.push(item) // 直接修改,不用 dispatch 也不用 set
}
}
// 组件里
@observer // 这个 HOC 让组件自动追踪它用到了哪些 observable
class CartIcon extends Component {
render() {
return <span>{this.props.cart.itemCount}</span>
// ↑ MobX 发现 component 用了 itemCount
// itemCount 依赖 items
// items 变了 → 自动重渲染这个组件
}
}
这写法和 Vue 的 reactive/computed 几乎一模一样------如果你学过 Vue,MobX 对你来说最自然。
优缺点
- 优点:代码最少 (直接
this.items.push),自动追踪依赖不需要手动选 - 缺点:"魔法感"太强------你不知道它什么时候更新,追踪链路是隐式的,出了问题不好调试。另外装饰器语法和 React Hooks 的哲学不太一致。
全方位对比表
| 维度 | useContext | Redux (RTK) | Zustand | MobX |
|---|---|---|---|---|
| 数据流 | 发布-订阅 | 单向 + dispatch | 单向 + set | 响应式自动追踪 |
| 修改状态 | setState | dispatch(action) | set() | 直接修改属性 |
| 细粒度订阅 | 不支持 | useSelector | 选择器 | 自动追踪 |
| 样板代码 | 极少 | 中等 | 极少 | 少 |
| 性能 | 差(全量重渲染) | 好 | 好 | 好 |
| 包体积 | 0(内置) | ~12KB | ~2KB | ~16KB |
| 中间件/插件 | 无 | 极强(redux-saga, thunk...) | 有(精简) | 有 |
| 学习曲线 | 低 | 中-高 | 低 | 中 |