Skip to content

状态管理

没用 Redux,没用 MobX,自己写了一个 Zustand 风格的 closure-based store。80+ 字段的全局状态,靠这套简单的机制管得服服帖帖。

设计选择

Claude Code 的状态管理不依赖任何第三方库,用闭包实现了类似 Zustand 的 store:

typescript
function createStore(initialState) {
  let state = initialState
  const listeners = new Set()

  return {
    getState: () => state,
    setState: (partial) => {
      state = { ...state, ...partial }
      listeners.forEach(l => l(state))
    },
    subscribe: (listener) => {
      listeners.add(listener)
      return () => listeners.delete(listener)
    }
  }
}

为什么不用 Zustand?大概是不想引入依赖。这种 store 的实现就几十行代码,自己写的好处是完全可控。

AppState 结构

80+ 个字段,覆盖应用的所有状态:

  • 配置:设置、verbose 模式、模型选择
  • UI 状态:展开/收起、overlay、选中项
  • 连接:远程/Bridge 连接状态
  • 权限:工具权限规则、Agent 定义
  • 任务:任务列表、文件历史
  • MCP:MCP 客户端连接
  • 通知:消息队列、todo 列表

所有状态都包裹在 DeepImmutable<T> 中——编译时防止意外修改。

React 集成

两个 Hook 完成状态消费:

typescript
// 选择性订阅,只在关心的字段变化时重渲染
const model = useAppState(s => s.model)

// 获取 setState 函数
const setState = useSetAppState()

底层用 useSyncExternalStore 而不是 Context。原因:Context 变化时所有消费者都重渲染,而 useSyncExternalStore 可以做选择性订阅,只有 selector 返回值变化时才触发重渲染。

对 80+ 字段的大状态来说,这个优化至关重要。

投机执行

一个有意思的设计——Speculation System:

系统会预测用户的下一步操作,提前执行。状态机有两个状态:

  • idle:等待中
  • active:正在执行投机操作,带 abort 能力

如果预测错了,abort 掉重来。如果猜对了,用户体验就是"瞬间响应"。