React 状态管理:选哪个?

这个问题我被问过不下十次。说实话,没有标准答案,但我可以分享一下这几年的观察。

先说结论

小型项目用 Context + useReducer 就够了。中大型项目,Zustand 或 Jotai。Redux?除非你团队已经用熟了,否则别折腾。

Context 的问题

Context 本身没问题,问题在于怎么用。很多人这么写:

const AppContext = createContext();

function AppProvider({ children }) {
  const [user, setUser] = useState(null);
  const [theme, setTheme] = useState('light');
  const [cart, setCart] = useState([]);

  return (
    <AppContext.Provider value={{ user, setUser, theme, setTheme, cart, setCart }}>
      {children}
    </AppContext.Provider>
  );
}

然后任何一处状态变了,所有消费这个 Context 的组件都会重渲染。这就是所谓的”Context 性能问题”。

解决方案:拆分 Context。

const UserContext = createContext();
const ThemeContext = createContext();
const CartContext = createContext();

// 各自独立,互不影响

或者用 useMemo 优化一下 value。

Zustand 怎么样

用了一段时间,感觉挺好的。主要优点:

  1. 代码量少,没有模板代码
  2. 不需要 Provider 包裹
  3. 可以在组件外使用
import { create } from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

// 任何地方都能用
useStore.getState().increment();

但有个问题,就是开发工具不如 Redux 那么完善。调试的时候有点不方便。

Jotai 的原子化思路

Jotai 是另一个思路,把状态拆成原子:

import { atom, useAtom } from 'jotai';

const countAtom = atom(0);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

非常像 React 原生的 useState,只是状态可以在组件间共享。我个人挺喜欢这种风格的。

Redux Toolkit

如果你团队已经在用 Redux,那 Redux Toolkit 确实比以前方便多了。但学习曲线还是在的,光是搞清楚 slice、thunk、middleware 就够喝一壶的。

新项目的话,个人觉得没必要。

最后

工具是次要的,关键是想清楚状态属于哪一层:

  • 组件内部状态 → useState
  • 组件树共享状态 → Context / Zustand / Jotai
  • 服务端状态 → React Query / SWR

把服务端状态也塞进全局状态管理,是我见过最常见的坑。