Zustand 很适合普通 React 项目,但未必适合 AI 项目前端

3 阅读5分钟

这不是说 Zustand 不好,而是 AI 项目前端会把很多“轻量方案”的边界很快打出来。

先说结论,免得大家看半天:

如果你做的是普通后台、普通配置页、普通业务表单,Zustand 很香。

但如果你做的是:

  • AI 聊天界面
  • Agent 工作台
  • 工具调用面板
  • 检索增强前端
  • 多区域联动的 Copilot UI

那你很可能会在写着写着的时候发现:

Zustand 解决了“起步成本”,但不一定解决“复杂业务如何持续长大”。

而这恰恰是 AI 项目前端最容易踩坑的地方。

为什么 Zustand 现在这么火

先承认现实,Zustand 火不是没道理。

它几乎满足了现代 React 开发者对状态库的全部直觉期待:

  • API 简单
  • 上手快
  • 没太多样板代码
  • 写起来比 Redux 轻很多
  • 比较符合“先干活再说”的开发节奏

所以很多人现在默认的选择已经不是 Redux,而是:

“要不要直接上 Zustand?”

这个判断在很多项目里都对。

问题是,AI 项目前端经常不是“很多项目”里的那一种。

AI 项目前端的复杂度,和普通页面不是一个量级

很多人低估了 AI 前端的状态复杂度。

你以为你在做的是一个聊天框,实际上你在管理的是一个微型运行时:

  • 消息历史
  • 流式返回
  • 当前阶段状态
  • 工具调用链
  • 失败重试
  • 日志追踪
  • 多面板同步
  • 会话隔离
  • 可能还有 memory、context、plan、step

这类状态有个特点:

不是“量多”,而是“关系复杂”。

而关系复杂时,最怕的不是代码长,而是:

  • 逻辑分散
  • 语义模糊
  • 副作用到处飞
  • 后续接手的人和 AI 都看不清边界

Zustand 最大的优点,也可能是它的隐性问题

Zustand 的优点是轻。

但“轻”这个特性,一旦进入复杂业务,也可能变成一种风险:

  • 什么都能往 store 里塞
  • 什么逻辑都能先写进去
  • 异步请求、派生状态、副作用、组件联动,很容易混着长

于是项目一开始通常是这样:

const useChatStore = create(set => ({
  messages: [],
  streamingText: '',
  isStreaming: false,
  setStreamingText: (text) => set({ streamingText: text }),
  addMessage: (message) =>
    set(state => ({ messages: [...state.messages, message] })),
}));

看起来非常舒服。

然后几周后,它可能慢慢长成这样:

const useChatStore = create((set, get) => ({
  messages: [],
  streamingText: '',
  isStreaming: false,
  toolCalls: [],
  logs: [],
  retries: 0,
  activeSessionId: '',
  contextWindow: [],
  memory: [],

  addMessage: () => {},
  appendStreaming: () => {},
  startToolCall: async () => {},
  completeToolCall: () => {},
  retry: async () => {},
  syncContext: () => {},
  rebuildMemory: () => {},
  fetchHistory: async () => {},
  hydrateSession: async () => {},
}));

注意,这不是 Zustand 的错。

这是因为它太顺手了,所以特别容易把“先写进去再说”变成长期默认策略。

AI 一参与开发,这个问题会被放大

这一点我体感特别强。

很多人现在都在这样开发:

  1. 自己先起个 store
  2. 让 Cursor / Copilot 补 action
  3. 让 AI 再补异步请求
  4. 再让 AI 补 UI 和联动

如果底层是一个越来越胖的 Zustand store,AI 很容易做两件事:

  • 继续把新逻辑往原 store 里堆
  • 或者另起一套 hooks / utils 来绕开已有结构

最后结果通常不是“不能跑”,而是:

项目越来越像一个能工作的临时产物。

这也是我后来更偏向 easy-model 的原因

easy-model 不是说一定比 Zustand 更轻。

它的优势在于:

它逼着你更早把业务边界说清楚。

比如 AI 会话、搜索工作区、Agent 执行流,你会更自然地写成:

class AgentSessionModel {
  messages: Message[] = [];
  streamingText = '';
  status: 'idle' | 'running' | 'streaming' = 'idle';
  toolCalls: ToolCall[] = [];
  logs: string[] = [];

  @loader.load(true)
  async run(input: string) {
    this.status = 'running';
    this.messages.push({ role: 'user', content: input });
    // ...
    this.status = 'streaming';
    // ...
    this.status = 'idle';
  }
}

这里最重要的不是 class 本身,而是它天然形成了几个好处:

  • 一个模型就是一个业务域
  • 字段就是状态
  • 方法就是行为
  • 组件只负责消费
  • AI 也更容易看懂“新逻辑该往哪放”

Zustand 适合什么,easy-model 又适合什么

我现在的理解大概是:

Zustand 更适合

  • 小中型 React 页面
  • UI 状态为主的项目
  • 快速验证想法
  • 团队希望极低心智负担起步

easy-model 更适合

  • AI 项目前端
  • 业务状态和行为耦合很强的页面
  • 多组件共享同一业务实例
  • 需要让 AI coding assistant 长期参与开发
  • 希望项目越长越像“领域模型”,而不是“越来越胖的 store”

真正值得关注的不是“谁更火”,而是谁更扛业务

现在社区里 Zustand 确实更热。

但技术选择最怕的一件事,就是把“社区热度”错当成“业务适配度”。

尤其是 AI 项目前端,这类项目的复杂度非常容易后置爆发。

前两周你会觉得 Zustand 写得真爽。

第六周你可能会开始问:

  • 为什么这个 store 已经这么大了?
  • 为什么请求逻辑和 UI 联动逻辑缠在一起?
  • 为什么 AI 每次改代码都在不同地方补丁?
  • 为什么我越来越不敢让它继续自动改?

这个时候你才会发现,真正贵的不是起步那一天,而是后面每一次扩展。

最后

所以我的观点不是:

“别用 Zustand。”

而是:

“如果你做的是 AI 项目前端,别默认 Zustand 一定就是最优解。”

至少,把 easy-model 这种更强调业务建模和 AI 可理解性的方案也一起放进候选列表里。

因为未来前端架构要回答的问题,已经不只是:

  • 人类开发者好不好写

还包括:

  • AI 好不好理解
  • AI 好不好续写
  • 项目复杂之后会不会快速失控

项目地址:

如果你也在做 AI 前端,或者最近正被 Zustand store 越长越胖这件事折磨,欢迎去看看。

顺手点个 Star,也算帮更多人看到另一种思路。