mcpField
S
state-management

state-management

github.com/statelyai开发48169 次安装24 周前更新
代码仓库

关于 state-management

面向开发者的应用状态建模技能,适用于设计、审查和重构状态。它先明确最小事实来源与有效转换,再映射到具体库。

主要功能

  1. 01分类源状态、派生状态、服务端状态和 URL 状态
  2. 02定义不变量与有限状态
  3. 03将更新建模为事件和转换
  4. 04选择合适的状态存放位置
  5. 05映射到常见状态库

适用场景

  1. 01为新功能设计状态
  2. 02检查代码中的重复或无效状态
  3. 03在保持行为的同时重构状态

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/statelyai/skills --skill state-management

安装后在 Claude Code 中输入/state-management确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/
evals.json
references/
async-state.md
no-library.md
react.md
redux.md
review-checklist.md
typescript-modeling.md
vue-pinia.md
zustand.md

详细介绍

状态管理

优先使用此技能进行状态建模,然后再映射到库。

目标不是推荐某个偏爱的库,而是构建一个可靠的状态模型,无论最终实现使用本地组件状态、reducer、Redux、Zustand、Pinia、URL 参数、TanStack Query,还是完全不使用库,都能保持稳健。

你的工作:

  • 将状态缩减为最小且有效的事实来源
  • 将派生值移至选择器、getter、计算属性或渲染时计算
  • 当多个布尔值代表互斥模式时,用明确的有限状态替代布尔值混杂
  • 通过事件和状态转换定义状态变化,而不是随意调用 setter
  • 将每个状态放在合适的位置
  • 检查现有代码中的无效状态、重复数据、同步错误和放置不当的副作用
  • 只有在模型清晰后,才将其映射到用户选择的库

第一轮检查

在现有代码库中:

  1. 检查本地 package.json 文件和导入语句。
  2. 阅读问题附近的组件、store、reducer 和选择器/getter 代码。
  3. 确定哪些状态是本地状态、全局状态、远程状态、URL 状态或外部状态。
  4. 在提出更改建议前,将当前实现细节与实际领域模型区分开来。

如果用户希望从零开始设计,则直接对领域建模,然后再选择实现方式。

任务模式

回答前选择一种模式:

  • 设计:根据需求构建新的状态模型。
  • 审查:找出错误、风险、无效状态和放置不当的状态。
  • 重构:在保持行为不变的同时简化模型并强化不变量。
  • 库映射:将已有的可靠模型转换为 React、Redux、Zustand、Vue/Pinia 或纯函数实现。

如果真正的问题是模型有缺陷,不要直接跳到 API 建议。

核心分类

在决定每个值的归属前,先对其进行分类。

  • 源状态:应用必须存储的权威事实。
  • 派生状态:可根据源状态和 props/输入计算得出的值。除非有充分的边界原因,否则不要存储这些值。
  • 有限状态:互斥模式,例如 idle、editing、submitting、success、failure。
  • 服务端状态:涉及加载、新鲜度、缓存和失效处理的远程数据。
  • URL 状态:应在刷新后保留、支持分享或参与前进/后退导航的值。
  • 临时可变值:计时器、DOM 句柄、AbortController、之前的快照、订阅以及其他非视觉可变值。
  • 历史状态:撤销/重做栈、审计日志、之前的状态和快照。

如果某个值无法明确归类,你可能还没有理解它的作用。

核心原则

  • 保持源状态最少。
  • 每个事实优先只使用一个事实来源。
  • 计算派生值,而不是同步它们。
  • 对互斥模式使用一个有限的 status 字段或判别联合表示,而不是使用多个布尔值。
  • 让无效状态难以表示,或无法表示。
  • 将更新建模为领域事件和状态转换。
  • 将副作用放在边界处。副作用应响应状态转换,而不是充当主要协调机制。
  • 状态应尽可能放低,但在必要时也要放高。
  • 区分客户端状态、服务端缓存和 URL 状态。
  • 如果多个值总是一起变化,就明确建模它们之间的关系。

工作流

除非用户明确要求更窄的范围,否则请按此顺序进行。

  1. 列出所有当前或计划中的状态值。
  2. 使用上述分类法对每个值进行分类。
  3. 消除重复:
    • 删除镜像属性
    • 删除可推导出的值
    • 除非有明确的缓存边界,否则不要同时存储对象及其选中的 ID
  4. 定义不变量:
    • 哪些组合有效?
    • 哪些组合不可能出现?
    • 每种模式下必须始终满足什么条件?
  5. 识别有限模式:
    • 如果多个布尔值实际描述的是同一种状态,请将它们合并为有限状态
  6. 列出领域事件:
    • 可能发生什么?
    • 由谁或什么触发?
    • 事件携带哪些数据?
  7. 梳理状态转换:
    • 每个事件在哪些状态下有效?
    • 会发生什么变化?
    • 在边界处会产生哪些副作用?
  8. 决定状态的存放位置:
    • 本地组件状态
    • reducer/store
    • URL
    • 服务端缓存/查询层
    • ref 或外部可变对象
  9. 将模型映射到选定的库。

需求不明确时,先展示模型,再编写代码。

建模问题

编写或修改代码前,请先思考以下问题:

  • 我们实际需要存储的最小事实是什么?
  • 哪些值是这些事实的派生视图?
  • 哪些模式互斥?
  • 当前可以表示哪些不可能出现的状态?
  • 可能发生哪些事件?每种状态下哪些事件有效?
  • 页面导航或刷新后,这个状态是否需要保留?
  • 这真的是应用状态,还是服务端缓存?
  • 这个值是渲染所必需的,还是临时可变句柄?
  • 谁拥有这个状态?谁有权更改它?
  • 我们存储的是实现细节,还是领域事实?
  • 是否在使用副作用同步本应派生出的状态?

选择表示方式

优先选择能保持不变量的最简单表示方式。

  • 独立的本地值:只有字段确实相互独立时,才使用本地状态。
  • 有限模式:使用字符串字面量状态或判别联合,而不是多个布尔值。
  • 协调更新:多个字段需要同时更改,或事件处理取决于当前状态时,使用 reducer。
  • 复杂工作流:存在守卫条件、重试、取消、并发或子进程时,使用状态机或 actor 模型。
  • 关系型集合:嵌套更新变得繁琐或容易不一致时,对实体进行规范化。
  • 派生读取:使用选择器、getter、计算值或渲染时计算。
  • 服务端数据:优先使用查询/缓存工具,而不是将远程生命周期相关逻辑强行放入 UI store。
  • 不参与渲染的可变数据:使用 refs 或外部可变对象。

除非更简单的抽象无法强制实施所需的不变量,否则不要建议使用更复杂的抽象。

状态存放位置指南

选择状态的存放位置时,请考虑:

  • 谁会读取它?
  • 谁会写入它?
  • 它需要存续多久?
  • 刷新后是否需要保留?
  • 是否需要通过 URL 分享?
  • 它是权威应用状态,还是远程缓存?

推荐的默认做法:

  • 将临时的纯视图状态保留在本地
  • 只有在多个使用方确实需要共享跨组件客户端状态时,才将其放入 reducer/store
  • 将可分享的导航/筛选状态保存在 URL 中
  • 将获取的数据保存在服务端状态/缓存层中
  • 不要将非视觉句柄放入渲染状态

反模式

在评审和重构中,积极指出以下问题:

  • 将派生状态存储起来,并通过副作用重新同步
  • 在本地状态中镜像 props
  • 用多个布尔值描述同一种模式
  • 没有明确理由却同时存储 selectedItem 和 selectedItemId
  • 在组件、store 和 URL 中重复存储状态
  • 描述 setter 而非领域事件的操作名称
  • 应当规范化却使用深层嵌套的关系状态
  • 用 useEffect 链协调业务逻辑
  • 没有充分理由,却将大部分表单状态放入全局 store
  • 用一堆可空字段代替显式状态
  • 将服务端状态、客户端状态和请求生命周期混在一个无法区分的对象中

参考资料路由

仅打开与当前任务相关的参考资料:

  • React:references/react.md
  • Redux 和 Redux Toolkit:references/redux.md
  • Zustand:references/zustand.md
  • Vue 和 Pinia:references/vue-pinia.md
  • 原生 JavaScript 或与库无关的实现:references/no-library.md
  • TypeScript 建模与穷尽性检查:references/typescript-modeling.md
  • 异步生命周期建模:references/async-state.md
  • 评审流程与代码异味检测:references/review-checklist.md

如果问题明确涉及已使用 XState 或 Actor 系统的代码库,请优先使用专门的 xstate-v5 技能获取实现细节。

输出格式

建议采用以下回答结构:

  1. 状态清单:当前存在或必须存在的内容。
  2. 发现的问题或不变量:重复、无效状态和设计约束。
  3. 推荐模型:
    • 源状态
    • 派生状态
    • 有限状态或判别联合
    • 事件和转换
    • 状态放置位置
  4. 库的映射:仅当用户要求针对特定技术栈实现时提供。
  5. 代码:仅在模型足够清晰后提供。

对于评审任务,先列出具体问题。对于设计任务,先介绍模型。

最终自检

回答前,请确认:

  • 每个存储的值确实都需要存储
  • 派生值没有被镜像为源状态
  • 互斥模式没有被表示成互不相关的布尔值
  • 无效状态已减少或消除
  • 状态的所有权和放置位置明确
  • 副作用没有代替选择器或状态转换来处理工作
  • 异步状态已明确建模
  • 推荐的抽象没有超出问题所需的复杂度
  • 针对特定库的建议与用户实际使用的技术栈相符

评论

支持 Markdown