React 的状态管理哲学:从 Redux 到 Zustand 与 Recoil
从“到底该不该上状态库、该选哪个”这个长期争论出发,用 React 的设计哲学拆解状态管理的本质问题:共享范围、更新频率、可预测性与调试能力;对比 Redux(集中式 + 可追溯)、Zustand(轻量 store + selector)、Recoil(原子依赖图),并给出可落地的选型准则与迁移策略
React 状态管理是一个“越写越像宗教战争”的话题:
- 有人说:Redux 太重,别用
- 有人说:不用 Redux 迟早写成屎山
- 有人说:Zustand 才是现代答案
- 也有人说:Recoil 的原子模型才是未来
真相是:没有银弹。
你不该从“库好不好”出发选型,而应该从一个更本质的问题出发:你要管理的状态到底是什么?它的生命周期、共享范围、更新频率与调试需求是什么?
这篇我们用“问题驱动”的方式,把 Redux/Zustand/Recoil 放回它们要解决的矛盾里,你会得到一套能长期使用的选型框架。
0. 先把状态分三类:这一步决定你 80% 的架构
你可以把 React 应用里的状态粗分为:
0.1 UI 局部状态(Local UI State)
- 输入框内容
- 弹窗开关
- tab 选中项
- hover / focus
特点:
- 生命周期短
- 只在局部组件树内使用
- 更新频率高
- 不需要全局共享
默认方案:useState/useReducer + 组件拆分(最稳)
0.2 模块共享状态(Shared Module State)
- 当前页面的筛选条件 + 列表数据
- 一个编辑器模块的选区/面板状态
- 多个兄弟组件共享的“工作区状态”
特点:
- 共享范围有限(通常是一个页面或模块)
- 需要跨层通信,但不一定跨路由
- 常常伴随副作用(请求、缓存、分页)
默认方案:useReducer + Context(Provider 下沉)(你前面已经学过)
0.3 应用级全局状态(App/Global State)
- 用户信息、登录态、权限
- 全局配置、主题、多语言
- 全局消息队列(toast)
- 多页面共享的业务实体缓存(如商品、订单、会话)
特点:
- 生命周期长
- 共享范围大(跨路由、跨页面)
- 需要更强的“可控性”(调试、追踪、持久化)
- 容易出现并发更新与一致性问题
这类状态才是状态库真正的主战场。
记住一句话:不要为了“看起来专业”把 Local/Module 状态塞进全局 store。
全局化是有成本的:心智负担、重渲染、耦合与调试复杂度。
1. Redux 为什么能成为“曾经唯一答案”?它解决的是“可预测性与可追溯性”
Redux 的出现背景可以理解为:
大型前端应用里,状态变化散落在各处,时间线不可追踪,Bug 难复现难定位。
Redux 用三板斧解决:
- 单一数据源(single store):状态集中
- 纯函数 reducer:变化可预测
- action 日志:每一次变化都是可记录的事件
1.1 Redux 的真正价值:时间线与因果可见
当你的系统复杂到:
- 一个动作会引起多个模块联动
- Bug 只在某个操作序列出现
- 你需要“回放现场” 那么 Redux 的 DevTools(时间旅行/日志)是非常强的生产力。
1.2 Redux 的代价:样板与“全局化倾向”
经典 Redux 会让很多人反感:
- action types
- action creators
- reducer 组织
- connect/useSelector/useDispatch 的样板
Redux Toolkit 很大程度缓解了样板,但 Redux 的哲学仍是:
把变化集中管理,优先可控性与可追溯性。
这也意味着它更适合:
- 大团队协作
- 强调可调试与规范
- 业务状态复杂、联动多的应用
2. Zustand 为什么受欢迎?它解决的是“轻量共享 + 更贴近 React 的使用方式”
Zustand(以“轻量 store”著称)的体验常被形容为:
- 写起来像 hooks
- 上手成本低
- 样板少
- selector 很好用(只订阅需要的字段,降低重渲染)
2.1 它的核心哲学:小而直接
很多项目的真实需求是:
- 我确实需要一个跨组件共享的 store
- 但我不想为此引入一整套 action/reducer 体系
- 我更希望“像写 hooks 一样写状态”
Zustand 恰好满足:
- 全局或模块 store 都可以
- store 里放状态 + 方法
- 组件按 selector 订阅(减少无效更新)
这使它非常适合:
- 中小团队快速迭代
- 需要共享但不想引入重框架
- 希望尽快获得“跨组件共享 + 性能可控”的平衡
2.2 它的代价:规范与追溯能力需要你自己补
Zustand 可以很自由,但自由也意味着:
- 你需要自己约束 store 结构
- 复杂联动时,调试时间线不如 Redux 原生强
- “随手写在 store 里”的副作用逻辑可能逐渐膨胀
所以 Zustand 更像:
给你一把很锋利的小刀,切得很爽,但切大工程要靠纪律。
3. Recoil 的价值:原子模型 + 依赖图(更像“数据流系统”)
Recoil 的核心思想是:
- 状态拆成很多“原子”(atom)
- 派生状态用 selector 表达
- 组件订阅 atom/selector
- 形成一张依赖图:谁依赖谁、谁变了会影响谁
3.1 它解决的矛盾:全局 store 的“粗粒度更新”
在 Redux 的“单棵大树”里,即使 useSelector 能局部订阅,你仍然在思维上面对:
- 一个巨大 state tree
- reducer 负责很多领域逻辑
- 派生值要么写 selector,要么组件里算
Recoil 的模型更像:
让状态天然是细粒度的,组件只拿自己关心的“原子”,派生关系由 selector 描述。
这对一些场景很友好:
- 派生状态多、依赖关系复杂
- 多个视图共享一部分原子,又各自有派生
- 希望“像数据流图”一样组织状态
3.2 代价:概念更多、生态与心智成本更高
原子模型看似优雅,但你需要:
- 把状态拆粒度(拆不好就难维护)
- 理解 selector 的缓存与依赖更新
- 接受更“框架化”的模型
所以 Recoil 通常更适合:
- 状态依赖图很复杂的交互应用
- 你愿意为模型付出学习成本
4. 选型核心:看这 4 个维度,而不是看“谁更火”
你可以用这四个维度做决策:
4.1 共享范围(Scope)
- 只在组件内:useState/useReducer
- 模块内共享:useReducer + Context / 小 store(Zustand)
- 跨路由共享:状态库(Redux/Zustand/Recoil)
4.2 更新频率(Frequency)
- 高频(输入、拖拽、实时更新):需要更强的“选择性订阅”
- Zustand/Recoil 往往更轻松
- Redux 也能做到,但要写好 selector
4.3 可预测性与约束(Predictability)
- 需要强规范与一致性:Redux(尤其是大团队)
- 允许更灵活:Zustand
4.4 调试与可追溯(Debuggability)
- 需要时间线回放、动作日志:Redux 优势明显
- 可以接受较弱的时间线:Zustand
- 更偏依赖图与派生调试:Recoil(但也需要工具与经验)
5. 一个非常实用的“默认选项”(不想纠结时就按这个来)
你可以把它当成一套渐进路线:
- 先用 React 原生:useState/useReducer + Context(Provider 下沉)
- 当出现明确痛点(跨路由共享、性能瓶颈、调试困难)再引入状态库
- 库的选择:
- 需要强规范 + 时间线:Redux(RTK)
- 想轻量共享 + selector:Zustand
- 依赖派生复杂、想原子化:Recoil
这套路线能避免“早早全局化”带来的长期负担。
6. 迁移策略:怎么从“纯 React”平滑走向状态库?
6.1 不要一口吃成胖子:先从“最稳定的全局状态”开始
建议先迁移:
- auth(用户/权限)
- theme/i18n
- 全局消息/弹窗控制
因为它们:
- 生命周期长
- 共享范围大
- 业务耦合相对低
6.2 模块状态尽量模块化(避免 store 巨石化)
就算用状态库,也建议:
- 按领域/模块拆 store(或拆 slice/atom)
- 保持边界明确
- 不要把页面局部状态全部“上交全局”
6.3 保持派生逻辑集中
- Redux:selector
- Zustand:selector + memo 化派生
- Recoil:selector(天然)
避免把派生散落在组件里,导致重复计算与不一致。
7. 常见误区:把“数据请求状态”当成普通状态管理
很多应用里最复杂的状态其实是:
- 服务器数据缓存
- 请求去重、取消
- 过期与重验证
- 乐观更新
这类问题往往更适合专门的数据请求库(如 React Query/SWR 等)去处理,而不是自己在 Redux/Zustand 里手撸一套缓存系统。
你可以把它当成一个原则:
服务端状态(Server State)与客户端状态(Client State)最好分开治理。
状态库主要管 Client State,数据请求库主要管 Server State。
8. 本文小结:状态管理的“哲学”其实是边界管理
你应该带走的不是“某个库的优劣”,而是一套边界判断:
- 先按 Local / Module / Global 分类状态
- 共享范围越大、生命周期越长、联动越复杂,就越需要更强的约束与工具
- Redux 强在可追溯与规范;Zustand 强在轻量与选择性订阅;Recoil 强在原子依赖图
- 不要过早全局化;用痛点驱动引入复杂度
- Server State 不要硬塞进状态库,交给专门的数据层工具更合适
留言讨论
Discussion
欢迎交流与反馈