React 状态管理 2026:Context、Zustand、Jotai 还是 Redux Toolkit?
2018 年开新项目,你用 Redux。2020 年酷点的人用 Recoil。2022 年 Zustand 和 Jotai 二分天下。2026 年呢?React Compiler 帮你默默 memo 了一切——"用哪个状态库"这个问题不再是宗教战争。这是一份在生产里真用过的选型地图。
先建立心智模型
React 应用里的状态住在四个地方:
- 组件本地状态(
useState、useReducer)——单组件独享,不共享。 - 服务端状态——存在后端的东西。归 TanStack Query / SWR 管,不是状态库的事。
- URL 状态——"当前页"、"选中的筛选"、"打开的 tab"。URL 是天然的真相源。
- 客户端全局状态——其他一切:主题、登录用户、购物车、表单草稿、多步向导。
人们常犯的错误是把 1、2、3 都塞进全局状态库。别这么干。每种状态都有更合适的工具。状态库只该管第 4 类。
决策树速查
状态归谁管?
├─ 服务端 → TanStack Query / SWR,就到这。
└─ 客户端 → 适合放进 URL 吗?
├─ 适合 → useSearchParams / nuqs
└─ 不适合 → 多少组件用到?
├─ 1-2 个邻近 → useState + 传 props 即可
├─ 3+ 跨子树 → Zustand 或 Jotai
└─ 跨业务 + 大量派生 → Redux Toolkit(少数情况)
推荐顺序
Zustand — 新的默认
70 行代码,无 Provider、无样板、TypeScript 友好。新项目今天开工又没什么特别理由,就选它。
import { create } from 'zustand'; export const useCart = create((set) => ({ items: [], add: (id) => set((s) => ({ items: [...s.items, { id, qty: 1 }] })), clear: () => set({ items: [] }), }));
组件里直接用:
const items = useCart((s) => s.items);
selector 模式(传一个函数)是避免无关重渲染的关键。学会这个习惯,库就用对了 90%。
Jotai — 细粒度反应式
Jotai 模型反过来:很多小 atom,组件订阅自己关心的 atom。
适合:表单字段、大量独立开关、复杂派生值的场景。
不适合:你更想要一个集中 store + 命名 action。这种 Zustand 更合适。
Context — 真正树作用域的状态
useContext 名声不好其实冤枉。问题不是 context 本身,是把"高频变动的值"塞进去且没拆分。Context 适合很少变化的依赖:
- 当前 locale
- 登录后的用户对象
- 配置好的 client(Apollo、Axios)
- 主题
不适合:每按键改变、每滚动改变的东西。
Redux Toolkit — 仍是某些场景的正解
别翻白眼。RTK 在这些场景仍是对的:
- 大团队,需要严格、可时间回溯的约定。
- 数据高度规范化、有大量跨实体更新(离线优先 CRM 那种)。
- 你接手的就是个 Redux 项目,迁到 Zustand 不划算。
小团队 + C 端应用,RTK 是过度设计。
React Compiler 改变了什么
React Compiler(原 React Forget)自动 memo 组件和值。结果:
- "大 Context + 频繁更新"和"拆分 selector store"之间的性能差距缩小了。
- 大多数代码不再需要手写
useMemo/useCallback。 - Zustand / Jotai / Context 的选择更看人体工学而不是避免重渲染。
但这不意味着 Context 成了万能答案。顶层 Provider 一秒重渲染 60 次,编译器救不了你。
从 Redux 迁到 Zustand 的实战路径
不要一次性大改造,按下面增量做:
- 只给新功能建 Zustand store,老的 slice 不动。
- 当老 slice 有实质修改时再迁。
- 老 selector 用户清零后删掉 Redux 代码。
多数团队一个季度搞定,不是一个 sprint。
反模式
- 表单状态走全局 store。 用 react-hook-form 或
useReducer。 - 把服务端数据塞 Zustand 自己手动 refetch。 TanStack Query 更省事。
- "单一 store"教条。 这是 2016 年没得选时的产物,现在是宗教信仰。
- 忘写 selector 导致整页重渲染。 客户端状态密集应用的头号性能问题。
一句话总结
- 新项目默认 Zustand。
- 状态天然由很多独立小块组成时用 Jotai。
- 树作用域、慢速变化的依赖用 Context。
- 老项目或强约定团队用 Redux Toolkit。
- 服务端数据永远用 TanStack Query / SWR,不是状态库。
- React Compiler 让选型场地变平,按人体工学选就行。
经验法则:你在两个选项之间犹豫超过 30 分钟,已经在做错的权衡了——选一个开始干,切换成本永远低于分析瘫痪的成本。
相关阅读
Vue 3 vs React 2026:下个项目的诚实对比
2026 年 Vue 3 与 React 的诚实对比 — Composition API vs Hooks、性能、生态、TypeScript 表现,以及真正决定选型的标准。
Flutter 状态管理选型 2026:Riverpod vs Bloc vs setState
2026 年 Flutter 状态管理诚实对比 — 什么时候 setState 就够、Riverpod 凭什么成了新项目默认、Bloc 在哪里仍然值回它的复杂度、Signals 怎么定位。
TypeScript 类型体操:什么时候值,什么时候在炫技
务实的 TypeScript 高级类型指南 — mapped types、conditional types、template literal types 真正能给你什么,什么时候用,什么时候应该退回到朴素代码。