首页技术专题博客目录我的收藏关于与联系

状态管理的混乱:从 Redux 到 Zustand 的迁移

项目里同时用了 Redux、Context API、Zustand 和本地状态,状态管理乱成一锅粥。花了两个月统一到 Zustand,这篇文章记录一下迁移过程和踩过的坑。

混乱的开始

项目是 3 年前开始的,那时候 Redux 还是主流。后来新人进来,有人用 Context API,有人用 Zustand,还有人直接用 useState。时间一长,状态管理就乱套了。

最夸张的时候,一个用户信息,在 Redux store、Context、Zustand store 和组件本地状态里各存了一份。改个用户名,要同步 4 个地方,经常漏掉。

为什么选 Zustand?

决定统一状态管理的时候,考虑了几个方案:

  • Redux:太复杂,写个简单状态要写一堆 boilerplate。
  • Context API:性能不好,容易导致不必要的重渲染。
  • Jotai:原子化状态,但学习成本高。
  • Zustand:简单,性能好,TypeScript 支持好。

最后选了 Zustand,主要是因为它够简单。写个 store 就几行代码,不用配置 middleware,不用写 action creator,直接用。

迁移过程

第一步:梳理现有状态

先花了一周时间,把项目里所有的状态都列出来,看看哪些是全局的,哪些是局部的,哪些是重复的。

结果发现,80% 的状态其实都是局部的,根本不需要全局管理。只有用户信息、主题设置、通知这些真正需要全局共享。

第二步:按模块迁移

我们按功能模块一个个迁移,先迁移用户模块,再迁移主题,最后迁移通知。每个模块迁移完,都要测试一遍,确保功能正常。

迁移 Redux 的时候最麻烦,因为 Redux 的 action 和 reducer 分散在各个文件里,要找全不容易。我们写了个脚本,扫描所有 Redux 相关的 import,才把依赖关系理清楚。

第三步:清理重复状态

迁移过程中,发现很多状态是重复的。比如用户信息,在 Redux 和 Context 里都有。我们统一到一个 Zustand store 里,然后逐步替换所有引用。

遇到的坑

坑一:Redux DevTools 依赖

有些同事习惯用 Redux DevTools 调试,迁移到 Zustand 后就用不了了。虽然 Zustand 也支持 Redux DevTools,但配置起来有点麻烦。

后来我们用了 Zustand 的 middleware,加上 Redux DevTools 支持,才解决了这个问题。

坑二:中间件迁移

Redux 里用了 redux-thunk 和 redux-persist,迁移到 Zustand 后,这些都要重新实现。

redux-thunk 的功能,Zustand 原生就支持,直接用 async 函数就行。redux-persist 我们用 zustand-persist 替代,API 差不多,迁移还算顺利。

坑三:类型推导

Redux 的类型推导很复杂,经常要手动写类型。Zustand 的类型推导好很多,但有些复杂的 store 结构,类型还是会有问题。

我们统一了 store 的结构规范,用 TypeScript 的 utility types 简化类型定义,才解决了这个问题。

迁移后的效果

迁移完成后,代码量减少了大概 30%。主要是 Redux 的 boilerplate 代码没了,状态管理逻辑也更清晰了。

性能也有提升。Zustand 的 selector 机制,比 Redux 的 connect 更精确,减少了不必要的重渲染。

现在的状态管理规范

迁移完成后,我们定了几个规范:

  • 全局状态用 Zustand,局部状态用 useState。
  • 每个功能模块一个 store,不要把所有状态放一个 store 里。
  • 复杂的状态逻辑,用自定义 hook 封装。
  • 异步操作直接用 async/await,不用 thunk。

如果重新开始

如果重新开始一个项目,我会:

  • 一开始就统一状态管理方案,不要混用。
  • 优先用局部状态,只有真正需要共享的才用全局状态。
  • 选简单好用的方案,不要过度设计。

总结

状态管理混乱是很多项目的通病。一开始没规划好,后面再统一就很麻烦。但统一是必要的,不然维护成本会越来越高。

如果你们项目也有类似问题,建议早点统一。虽然迁移有成本,但长痛不如短痛。选个简单好用的方案,定好规范,严格执行,问题就能解决。

评论区