创作者: Yardon | GitHub: github.com/YardonYan | 版本: v1.0
什么时候需要状态管理
先泼一盆冷水:大多数 React 应用不需要 Redux。
这句话不是我说的,是 Redux 的作者 Dan Abramov 本人说的。他在 2020 年就公开表示,在他参与的大多数项目中,Redux 已经不再是最好的选择。
那么什么时候真的需要「状态管理库」?
当你的应用满足以下任意一个条件时:
- 超过 3 层嵌套的组件需要共享同一个状态
- 多个不相关的页面需要访问同一份数据
- 状态变更的逻辑非常复杂(多个 action 之间有依赖关系)
- 需要时间旅行调试(undo/redo)
其他情况?useState + useContext 足够了。
React 自带的方案回顾
在介绍第三方库之前,先把 React 自带的工具说清楚。
Props Drilling(属性穿透)
最基础的方式就是通过 props 一层层往下传:
App → Header → NavBar → Logo
↓
UserMenu → Avatar → Dropdown → Item
如果 UserMenu 和 NavBar 都需要访问 currentUser,而 currentUser 定义在 App 组件里------你得把它一层层往下传。这在浅层嵌套时没问题,但如果你的组件树有 5-6 层深,每层都要"过手"一个跟它们毫无关系的 props,这就成了「属性穿透地狱」。
想象你在一栋公寓里,每层都住着互不相识的邻居,但暖气管道要穿过每一家的客厅。物业公司的员工(中间组件)被迫了解每家每户的暖气情况------尽管他只需要把暖气送到就行了。
Context API:内置的跨组件通信
Context 就是来解决这个问题的------让你在组件树的任意位置访问数据,而不需要手动层层传递。
Context API:轻量级全局状态
基本用法
jsx
// ThemeContext.jsx
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('dark');
function toggleTheme() {
setTheme(t => t === 'dark' ? 'light' : 'dark');
}
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) throw new Error('useTheme 必须在 ThemeProvider 内使用');
return context;
}
jsx
// App.jsx
import { ThemeProvider } from './ThemeContext';
function App() {
return (
<ThemeProvider>
<Dashboard />
</ThemeProvider>
);
}
// 任意子组件中
function SettingsPage() {
const { theme, toggleTheme } = useTheme();
return <button onClick={toggleTheme}>当前: {theme}</button>;
}
Context 的性能陷阱
Context 最大的坑在于:Provider 下的所有组件都会在 Context 值变化时重新渲染。
jsx
// ❌ 每秒更新一次,所有消费者都 re-render
function App() {
const [time, setTime] = useState(Date.now());
useEffect(() => {
const id = setInterval(() => setTime(Date.now()), 1000);
return () => clearInterval(id);
}, []);
return <ThemeContext.Provider value={{ time }}>...</ThemeContext.Provider>;
}
// ✅ 只暴露必要的状态
function App() {
const [time, setTime] = useState(Date.now());
const { theme } = useThemeContext(); // 只订阅需要的
useEffect(() => {
const id = setInterval(() => setTime(Date.now()), 1000);
return () => clearInterval(id);
}, []);
return <ThemeContext.Provider value={{ theme, time }}>...</ThemeContext.Provider>;
}
最佳实践:按功能拆分成多个独立的 Context。
jsx
// 主题一个 Context,用户信息一个 Context,购物车一个 Context
// 每个 Context 只订阅它真正需要的数据
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={user}>
<CartContext.Provider value={cart}>
<App />
</CartContext.Provider>
</UserContext.Provider>
</ThemeContext.Provider>
这样只有购物车变了,Header 里的主题切换按钮才不会无辜地重新渲染。
Zustand:极简主义的代表
Zustand 是 2020 年后最火的状态管理库。它用起来非常简单------5 分钟就能上手。
核心概念
jsx
// store/useCartStore.js
import { create } from 'zustand';
const useCartStore = create((set, get) => ({
items: [],
total: 0,
addItem: (product) => set((state) => ({
items: [...state.items, product],
total: state.total + product.price,
})),
removeItem: (id) => set((state) => {
const item = state.items.find(i => i.id === id);
return {
items: state.items.filter(i => i.id !== id),
total: state.total - (item?.price || 0),
};
}),
clearCart: () => set({ items: [], total: 0 }),
// 直接读取 state,不需要 hooks
getItemCount: () => get().items.length,
}));
jsx
// 在组件中使用
function CartIcon() {
const itemCount = useCartStore(s => s.items.length);
return <span>🛒 {itemCount}</span>;
}
function CartPage() {
const { items, total, removeItem, clearCart } = useCartStore();
return (
<div>
{items.map(item => <CartItem key={item.id} item={item} onRemove={removeItem} />)}
<p>总计: ¥{total}</p>
<button onClick={clearCart}>清空购物车</button>
</div>
);
}
为什么选择 Zustand
| 特性 | Zustand | Redux Toolkit |
|---|---|---|
| 代码量 | 极简(~50行一个store) | 需要 action/reducer/store boilerplate |
| 学习曲线 | 几乎为零 | 中等(概念多) |
| DevTools | 支持 | 强大(时间旅行等) |
| 适用场景 | 中小型应用 | 大型复杂应用 |
| 包大小 | ~1KB | ~15KB |
Redux Toolkit:复杂应用的选择
Redux 曾经是 React 状态管理的代名词。2019 年 Redux Toolkit 的出现大幅降低了使用门槛,但它的复杂性依然高于 Zustand。
核心概念:Slice
javascript
// features/cart/cartSlice.js
import { createSlice } from '@reduxjs/toolkit';
const cartSlice = createSlice({
name: 'cart',
initialState: { items: [], total: 0 },
reducers: {
addItem: (state, action) => {
state.items.push(action.payload);
state.total += action.payload.price;
},
removeItem: (state, action) => {
const idx = state.items.findIndex(i => i.id === action.payload);
if (idx !== -1) {
state.total -= state.items[idx].price;
state.items.splice(idx, 1);
}
},
clearCart: (state) => {
state.items = [];
state.total = 0;
},
},
});
export const { addItem, removeItem, clearCart } = cartSlice.actions;
export default cartSlice.reducer;
javascript
// store/index.js
import { configureStore } from '@reduxjs/toolkit';
import cartReducer from './features/cart/cartSlice';
export const store = configureStore({
reducer: {
cart: cartReducer,
user: userReducer,
// ...
},
});
jsx
// 组件中使用
import { useSelector, useDispatch } from 'react-redux';
import { addItem } from './features/cart/cartSlice';
function ProductPage({ product }) {
const dispatch = useDispatch();
const isInCart = useSelector(s =>
s.cart.items.some(i => i.id === product.id)
);
return (
<div>
<h1>{product.name}</h1>
<button
disabled={isInCart}
onClick={() => dispatch(addItem(product))}
>
{isInCart ? '已在购物车' : '加入购物车'}
</button>
</div>
);
}
Redux Toolkit 适合什么时候?当你有多个开发者协作的大型项目,需要严格的状态流转规范、时间旅行调试、批量操作日志时,Redux 的约束反而是优点------它强制你用规范的方式做事,不容易出现"状态不知道从哪改的"的情况。
Jotai:原子化状态管理
Jotai 是一个相对小众但很有特色的方案。它的核心概念是原子(Atom)------最小的状态单元。
jsx
// atoms.js
import { atom } from 'jotai';
// 基础原子
const userAtom = atom(null);
const notificationCountAtom = atom(0);
// 派生原子(类似 useMemo)
const hasNotificationsAtom = atom(get => get(notificationCountAtom) > 0);
// 写原子
const addNotificationAtom = atom(
null,
(get, set) => {
const current = get(notificationCountAtom);
set(notificationCountAtom, current + 1);
}
);
jsx
// 组件中只订阅需要的原子
function NotificationBadge() {
const count = useAtom(notificationCountAtom)[0];
return count > 0 ? <span>{count}</span> : null;
}
function NotificationButton() {
const [, addNotification] = useAtom(addNotificationAtom);
return <button onClick={addNotification}>有新消息</button>;
}
Jotai 的优势是细粒度更新------只有真正用到某个原子值的组件才会重新渲染。它适合状态很多、更新很频繁的应用(比如实时协作工具、数据可视化面板)。
选型决策树
应用规模?
├── 小型(几个页面,状态简单)
│ └── useState + Context API ✅ 够了
│
├── 中型(10+ 页面,多人协作)
│ ├── 状态种类多、更新频繁
│ │ └── Zustand ✅ 推荐
│ │
│ └── 需要强规范、强调试能力
│ └── Redux Toolkit ✅
│
└── 大型(复杂状态流转、undo/redo)
└── Redux Toolkit ✅ 或 Recoil
我的推荐(2026 年):
- 90% 的项目:
useState+Context或Zustand - 5% 的项目需要强规范:
Redux Toolkit - 5% 的项目状态极细:
Jotai
本章小结
| 工具 | 适用场景 | 代码量 | 学习成本 |
|---|---|---|---|
| useState + Context | 小型应用、简单全局状态 | 少 | 无 |
| Context API(多 Context) | 中型应用、分域全局状态 | 中 | 低 |
| Zustand | 中小型应用、需要灵活状态 | 极少 | 极低 |
| Redux Toolkit | 大型、复杂、多人协作 | 多 | 中 |
| Jotai | 细粒度状态、频繁更新 | 少 | 中 |
状态管理的本质是数据的流向和共享 。选什么工具不重要,重要的是理解为什么需要它、它解决的是什么问题。下一章我们聊聊 React Router------单页应用如何实现页面跳转和 URL 管理。
📌 创作者: Yardon | 🏠 个人网站: GlimmerAI.top
📖 本章是「React 从入门到生产 」系列的第 5 章。上一章:自定义Hook | 下一章:路由与导航
🌟 如果你觉得有帮助,欢迎访问 GlimmerAI.top 查看我的更多作品。欢迎大家来观看!