React 状态管理 2026:Context、Zustand、Jotai 还是 Redux Toolkit?
说实话,上个项目的 Redux 样板我写到吐——一个加购物车的 action 要碰 actionType、reducer、selector、dispatch 四个文件,改个字段名能改半小时。所以现在我开新项目默认就是 Zustand,除非有人能给我一个非用 Redux 不可的理由。
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(少数情况)
四个候选横向对比
懒得看决策树的话,直接看这张表。下面这些"踩坑"是我自己或者团队同事真撞过的:
| 方案 | 适用规模 | 样板量 | 我踩过的坑 |
|---|---|---|---|
| Context | 几乎不变的树作用域值 | 几乎为零 | 把会高频更新的值塞进去,整棵子树一起重渲染,profiler 一拉全是红的 |
| Zustand | 中小项目、3+ 组件共享 | 很少,无 Provider | 忘了写 selector,订阅整个 store,改一个字段全页面重渲染 |
| Jotai | 大量独立小状态、表单字段 | 中等,atom 满天飞 | atom 拆太细,依赖关系绕成一团,后来人接手看不懂 |
| Redux Toolkit | 大团队、强规范、高度规范化数据 | 最多,但 RTK 已经砍了一半 | 小项目硬上,一个简单功能写出五个文件,杀鸡用牛刀 |
推荐顺序
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 分钟,已经在做错的权衡了——选一个开始干,切换成本永远低于分析瘫痪的成本。
我们是一支开发者小团队,负责构建和维护 ToolsForge —— 150+ 个完全在浏览器本地运行、隐私优先的工具。这些文章来自我们日常的工程实践,而不是纸上谈兵。
了解我们相关阅读
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 怎么定位。
一张照片能做多少事:AI 改图工具实战指南
换发型、换发色、换装、转卡通、风格迁移、室内设计、老照片修复——用一张照片走完这些 AI 改图流程,以及怎么让结果更稳、更像本人。