React 状态管理三强争霸:Zustand / Jotai / Redux 选型指南

By | 2026年9月9日

引言

在 React 应用开发中,状态管理始终是绕不开的话题。从早期的 Redux 一统天下,到如今 Zustand、Jotai 等轻量方案百花齐放,开发者面临的选择越来越多。但选择越多,困惑也越多:到底该用哪个?本文将从设计理念、使用体验、性能、生态等维度,对三大主流方案进行深度对比,并给出选型建议。

1. 背景与问题

假设我们要开发一个中等复杂度的电商应用,需要管理用户信息、购物车、商品列表等状态。如果用传统的 useState + props 层层传递,组件树会变得臃肿不堪;而引入状态管理库,又担心过度设计。这正是本文要解决的问题:如何根据项目规模、团队习惯、性能要求,选择最合适的状态管理方案。

2. 三大方案简介

2.1 Redux:经典中的经典

Redux 基于 Flux 架构,强调单一数据源、状态只读、使用纯函数 reducer 修改状态。它的核心概念包括 store、action、reducer、dispatch。配合 Redux Toolkit(RTK),开发体验已大幅提升。

2.2 Zustand:极简主义的新锐

Zustand 是一个轻量级状态管理库,API 极其简洁。它利用 React hooks 的特性,无需 Provider 包裹,直接通过 create 函数创建 store,在组件中通过 useStore 方法获取状态和操作。

2.3 Jotai:原子化状态管理

Jotai 采用原子模型,将状态拆分为一个个独立的 atom,通过组合 atom 构建复杂状态。它同样基于 hooks,但更强调状态的细粒度订阅,避免不必要的重渲染。

3. 实战对比:实现同样的功能

我们以实现一个购物车功能为例,对比三种方案的代码差异。

3.1 Redux 实现(使用 Redux Toolkit)

首先安装依赖:


npm install @reduxjs/toolkit react-redux

创建 store:


// store/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) => {
      state.items = state.items.filter(item => item.id !== action.payload.id);
      state.total -= action.payload.price;
    }
  }
});

export const { addItem, removeItem } = cartSlice.actions;
export default cartSlice.reducer;

配置 store:


// store/index.js
import { configureStore } from '@reduxjs/toolkit';
import cartReducer from './cartSlice';

export const store = configureStore({
  reducer: {
    cart: cartReducer
  }
});

在组件中使用:


// Cart.jsx
import { useSelector, useDispatch } from 'react-redux';
import { addItem, removeItem } from './store/cartSlice';

function Cart() {
  const cart = useSelector(state => state.cart);
  const dispatch = useDispatch();
  
  return (
    <div>
      <h3>购物车</h3>
      <ul>
        {cart.items.map(item => (
          <li key={item.id}>
            {item.name} - ${item.price}
            <button onClick={() => dispatch(removeItem(item))}>删除</button>
          </li>
        ))}
      </ul>
      <p>总价: ${cart.total}</p>
      <button onClick={() => dispatch(addItem({ id: 1, name: '商品', price: 10 }))}>添加</button>
    </div>
  );
}

3.2 Zustand 实现

安装:


npm install zustand

创建 store:


// store/cartStore.js
import { create } from 'zustand';

const useCartStore = create((set) => ({
  items: [],
  total: 0,
  addItem: (item) => set((state) => ({ 
    items: [...state.items, item], 
    total: state.total + item.price 
  })),
  removeItem: (item) => set((state) => ({
    items: state.items.filter(i => i.id !== item.id),
    total: state.total - item.price
  })),
}));

export default useCartStore;

在组件中使用:


// Cart.jsx
import useCartStore from './store/cartStore';

function Cart() {
  const items = useCartStore(state => state.items);
  const total = useCartStore(state => state.total);
  const addItem = useCartStore(state => state.addItem);
  const removeItem = useCartStore(state => state.removeItem);
  
  return (
    <div>
      <h3>购物车</h3>
      <ul>
        {items.map(item => (
          <li key={item.id}>
            {item.name} - ${item.price}
            <button onClick={() => removeItem(item)}>删除</button>
          </li>
        ))}
      </ul>
      <p>总价: ${total}</p>
      <button onClick={() => addItem({ id: 1, name: '商品', price: 10 })}>添加</button>
    </div>
  );
}

3.3 Jotai 实现

安装:


npm install jotai

创建 atom:


// store/cartAtoms.js
import { atom } from 'jotai';

export const itemsAtom = atom([]);
export const totalAtom = atom(0);

// 派生 atom:根据 items 计算总价
export const totalPriceAtom = atom((get) => 
  get(itemsAtom).reduce((sum, item) => sum + item.price, 0)
);

在组件中使用:


// Cart.jsx
import { useAtom } from 'jotai';
import { itemsAtom, totalPriceAtom } from './store/cartAtoms';

function Cart() {
  const [items, setItems] = useAtom(itemsAtom);
  const [total] = useAtom(totalPriceAtom);
  
  const addItem = (item) => setItems([...items, item]);
  const removeItem = (item) => setItems(items.filter(i => i.id !== item.id));
  
  return (
    <div>
      <h3>购物车</h3>
      <ul>
        {items.map(item => (
          <li key={item.id}>
            {item.name} - ${item.price}
            <button onClick={() => removeItem(item)}>删除</button>
          </li>
        ))}
      </ul>
      <p>总价: ${total}</p>
      <button onClick={() => addItem({ id: 1, name: '商品', price: 10 })}>添加</button>
    </div>
  );
}

4. 深入对比

4.1 学习曲线

  • Redux:概念多(action、reducer、dispatch、middleware),但 RTK 简化了模板代码。适合团队有 Flux 经验或需要严格规范的项目。
  • Zustand:API 极简,几乎零学习成本,适合快速开发。
  • Jotai:需要理解原子思维,但逻辑清晰,适合状态间有依赖的场景。

4.2 性能与渲染优化

  • Redux:默认使用浅比较,但 selector 需要手动优化(如使用 createSelector)。
  • Zustand:通过 useStore 的 selector 精确订阅,避免无关渲染。
  • Jotai:原子级订阅,只有依赖的 atom 变化时才会重渲染,性能最佳。

4.3 生态与工具

  • Redux:拥有庞大的中间件生态(如 redux-saga、redux-thunk)、DevTools 集成成熟。
  • Zustand:轻量,但中间件支持有限,DevTools 需额外配置。
  • Jotai:与 React 并发模式兼容良好,但生态较新。

4.4 可维护性

  • Redux:强制单向数据流,代码结构清晰,适合大型团队协作。
  • Zustand:灵活自由,但需自律,否则容易混乱。
  • Jotai:原子组合,适合复杂状态逻辑,但过度拆分可能增加认知负担。

5. 选型建议

5.1 项目规模与团队经验

  • 小型项目或快速原型:首选 Zustand,简单高效。
  • 中大型项目,团队熟悉 Redux:继续使用 Redux Toolkit,稳定可靠。
  • 状态依赖复杂,需要精细控制渲染:选择 Jotai。

5.2 性能敏感场景

如果应用有大量高频更新,Jotai 的原子订阅能最大程度减少重渲染。但实际性能差异通常不大,需用工具测量后再决定。

5.3 最佳实践

  • 避免过度设计:简单的 useState 能解决就不要用库。
  • 统一使用一种方案:不要混用,以免造成混乱。
  • 对于 Redux:务必使用 RTK,并配合 createEntityAdapter 处理集合。
  • 对于 Zustand:使用 shallow 比较或 selector 优化。
  • 对于 Jotai:利用 atomWithStorage 等工具持久化。

6. 踩坑经验

  • Redux 常见坑:忘记在 Provider 中传入 store;异步操作未使用中间件;selector 返回新对象导致无限循环。
  • Zustand 常见坑:在组件外使用 store 方法时,注意 getState 的时机;避免在渲染期间修改状态。
  • Jotai 常见坑:atom 定义在组件内部会导致状态重置;异步 atom 需正确处理 loading。

7. 总结

Redux 稳重、Zustand 轻快、Jotai 精细。没有绝对的王者,只有最适合你项目的方案。在选型时,请综合考虑团队背景、项目复杂度、性能需求。希望本文能帮你做出明智的决策。

延伸阅读: