React 从入门到生产(五):状态管理选型

创作者: 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 &#039;zustand&#039;;

const useCartStore = create((set, get) =&gt; ({
  items: [],
  total: 0,

  addItem: (product) =&gt; set((state) =&gt; ({
    items: [...state.items, product],
    total: state.total + product.price,
  })),

  removeItem: (id) =&gt; set((state) =&gt; {
    const item = state.items.find(i =&gt; i.id === id);
    return {
      items: state.items.filter(i =&gt; i.id !== id),
      total: state.total - (item?.price || 0),
    };
  }),

  clearCart: () =&gt; set({ items: [], total: 0 }),

  // 直接读取 state,不需要 hooks
  getItemCount: () =&gt; get().items.length,
}));
jsx 复制代码
// 在组件中使用
function CartIcon() {
  const itemCount = useCartStore(s =&gt; s.items.length);
  return &lt;span&gt;🛒 {itemCount}&lt;/span&gt;;
}

function CartPage() {
  const { items, total, removeItem, clearCart } = useCartStore();
  return (
    &lt;div&gt;
      {items.map(item =&gt; &lt;CartItem key={item.id} item={item} onRemove={removeItem} /&gt;)}
      &lt;p&gt;总计: ¥{total}&lt;/p&gt;
      &lt;button onClick={clearCart}&gt;清空购物车&lt;/button&gt;
    &lt;/div&gt;
  );
}

为什么选择 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 &#039;@reduxjs/toolkit&#039;;

const cartSlice = createSlice({
  name: &#039;cart&#039;,
  initialState: { items: [], total: 0 },
  reducers: {
    addItem: (state, action) =&gt; {
      state.items.push(action.payload);
      state.total += action.payload.price;
    },
    removeItem: (state, action) =&gt; {
      const idx = state.items.findIndex(i =&gt; i.id === action.payload);
      if (idx !== -1) {
        state.total -= state.items[idx].price;
        state.items.splice(idx, 1);
      }
    },
    clearCart: (state) =&gt; {
      state.items = [];
      state.total = 0;
    },
  },
});

export const { addItem, removeItem, clearCart } = cartSlice.actions;
export default cartSlice.reducer;
javascript 复制代码
// store/index.js
import { configureStore } from &#039;@reduxjs/toolkit&#039;;
import cartReducer from &#039;./features/cart/cartSlice&#039;;

export const store = configureStore({
  reducer: {
    cart: cartReducer,
    user: userReducer,
    // ...
  },
});
jsx 复制代码
// 组件中使用
import { useSelector, useDispatch } from &#039;react-redux&#039;;
import { addItem } from &#039;./features/cart/cartSlice&#039;;

function ProductPage({ product }) {
  const dispatch = useDispatch();
  const isInCart = useSelector(s =&gt; 
    s.cart.items.some(i =&gt; i.id === product.id)
  );

  return (
    &lt;div&gt;
      &lt;h1&gt;{product.name}&lt;/h1&gt;
      &lt;button
        disabled={isInCart}
        onClick={() =&gt; dispatch(addItem(product))}
      &gt;
        {isInCart ? &#039;已在购物车&#039; : &#039;加入购物车&#039;}
      &lt;/button&gt;
    &lt;/div&gt;
  );
}

Redux Toolkit 适合什么时候?当你有多个开发者协作的大型项目,需要严格的状态流转规范、时间旅行调试、批量操作日志时,Redux 的约束反而是优点------它强制你用规范的方式做事,不容易出现"状态不知道从哪改的"的情况。


Jotai:原子化状态管理

Jotai 是一个相对小众但很有特色的方案。它的核心概念是原子(Atom)------最小的状态单元。

jsx 复制代码
// atoms.js
import { atom } from &#039;jotai&#039;;

// 基础原子
const userAtom = atom(null);
const notificationCountAtom = atom(0);

// 派生原子(类似 useMemo)
const hasNotificationsAtom = atom(get =&gt; get(notificationCountAtom) &gt; 0);

// 写原子
const addNotificationAtom = atom(
  null,
  (get, set) =&gt; {
    const current = get(notificationCountAtom);
    set(notificationCountAtom, current + 1);
  }
);
jsx 复制代码
// 组件中只订阅需要的原子
function NotificationBadge() {
  const count = useAtom(notificationCountAtom)[0];
  return count &gt; 0 ? &lt;span&gt;{count}&lt;/span&gt; : null;
}

function NotificationButton() {
  const [, addNotification] = useAtom(addNotificationAtom);
  return &lt;button onClick={addNotification}&gt;有新消息&lt;/button&gt;;
}

Jotai 的优势是细粒度更新------只有真正用到某个原子值的组件才会重新渲染。它适合状态很多、更新很频繁的应用(比如实时协作工具、数据可视化面板)。


选型决策树

复制代码
应用规模?
├── 小型(几个页面,状态简单)
│   └── useState + Context API ✅ 够了
│
├── 中型(10+ 页面,多人协作)
│   ├── 状态种类多、更新频繁
│   │   └── Zustand ✅ 推荐
│   │
│   └── 需要强规范、强调试能力
│       └── Redux Toolkit ✅
│
└── 大型(复杂状态流转、undo/redo)
    └── Redux Toolkit ✅ 或 Recoil

我的推荐(2026 年):

  • 90% 的项目:useState + ContextZustand
  • 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 查看我的更多作品。欢迎大家来观看!

相关推荐
前端若水9 小时前
使用 IndexedDB 在客户端存储对话记录
java·前端·人工智能·python·机器学习
yqcoder9 小时前
图片跨域之谜:img 标签真的“畅通无阻”吗
前端·javascript
啦啦啦_99999 小时前
6. 网络优化方法之 学习率 优化/衰减策略
深度学习
未来智慧谷9 小时前
汉中首家OPC社区正式成立!未来智慧谷联合京东科技(汉中)数字经济产业园打造“一人公司”企业新生态
大数据·人工智能·科技
HIT_Weston9 小时前
89、【Agent】【OpenCode】glob 工具提示词(参数内容)
人工智能·agent·opencode
Omics Pro9 小时前
前沿学科:量子生物学!
大数据·数据库·人工智能·windows·redis·量子计算
卸任9 小时前
为Tiptap富文本编辑器增加Word导出功能
前端·react.js
阿正的梦工坊9 小时前
【Typescript】06-类型缩小与控制流分析
前端·javascript·typescript
不是山谷.:.9 小时前
前端零基础入门:WebSocket 全解析
前端·笔记·websocket·状态模式