关于 xstate-v5
xstate-v5 是一项严格的 Codex 技能,用于设计、实现、重构和审查 TypeScript 与 React 中的类型化 XState v5 状态机。它要求先规划状态图,并规范 Actor 边界、事件类型以及对 v4 旧模式的规避。
主要功能
- 01先设计状态图及相关文档
- 02严格遵循 XState v5 API 规范
- 03类型化事件、上下文与输入
- 04明确 invoke 和子 Actor 边界
- 05React Actor 上下文使用指引
适用场景
- 01规划新的 XState v5 状态机
- 02重构或审查 XState v5 代码
- 03迁移 XState v4 旧模式
- 04设计 React 应用中的 Actor 边界
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ johnlombardo-dev/ xstate-skill --skill xstate-v5安装后在 Claude Code 中输入/xstate-v5确认已加载。
详细介绍
xstate-v5(严格版)技能
使用严格、强类型的规则集,构建、重构和审查 XState v5 状态机(TypeScript 和 React)。
唯一事实来源: references/xstate-v5-rules.md(本仓库中的文件,从技能根目录解析)。如果本技能中的任何内容与规则文件冲突,请遵循规则文件。
内嵌的强制契约(Agent 必须遵守)
此处有意重复这些要求,以便 Agent 无需先加载外部参考资料即可遵守:
- 先设计,再编写代码:
- 实现前先产出规划材料(状态清单、事件目录、转换表、异步/Actor 映射、验收测试场景)。
- 附上边界/拆分决策记录(哪些内容拆分、哪些内容由主流程协调,以及原因)。
- 不采用巨型状态机架构:
- 无关领域(身份验证、提示通知、导航、工作流、传输/重试策略)不得全部建模在一个状态机中。
- 只有作为轻量级协调根节点或紧密耦合的应用外壳并行区域时,才可以使用单个状态机。
- 不使用镜像状态的 context 标志:
- 当
state.value已表示模式时,不要在 context 布尔值中重复表示模式(isLoading、isAuthenticated等)。 - 如果存在临时迁移标志,请记录其理由和移除计划。
- 当
- 严格遵循 XState v5:
- 使用
setup({...}).createMachine({...})。 - 在
setup(...)中实现actions/guards/actors。 - 只发送事件对象。
- 避免使用禁止的 v4 模式(
interpret、Machine、cond、send、pure、choose等)。
- 使用
- 明确划分异步操作和 Actor 边界:
- 请求/响应:使用带有
onDone和onError的invoke。 - 长期协作对象:使用
spawnChild/stopChild,并通过sendTo显式路由。
- 请求/响应:使用带有
失败即关闭规则:
- 如果任何细节不明确,请在编写或审查状态机代码前加载
references/xstate-v5-rules.md。 - 如果该文件缺失或无法读取,请停止并报告问题,不要猜测。
适用场景
当任务涉及以下内容时,使用此技能:
- XState v5 状态机/Actor(
xstate、@xstate/react) - 重构 XState 代码,使其具备更强的类型安全性并符合 v5 惯用写法
- 将 XState v4 模式(Machine/interpret/cond/send/pure/choose 等)迁移为 v5 等效写法
- 在使用 XState v5 状态机实现之前规划/设计状态图
- 设计 Actor 边界(invoke 与 spawnChild)以及通过
createActorContext进行 React 集成
硬性要求(强制执行)
- 所有状态机优先使用
setup({...}).createMachine({...})。 - 在
setup({...})对象中实现所有actions、guards和actors。 - 优先通过类型化的
params向 actions/guards 传递从事件派生的数据。 - 绝不发送字符串事件。始终发送事件对象:
actor.send({ type: '...' })。 - 禁止使用
references/xstate-v5-rules.md中列出的 XState v4 旧版 API 和模式。 - 不要在一个状态机中建模无关领域(身份验证、通知/提示、导航、数据生命周期等),轻量级协调状态机除外。
- 当
state.value已表示模式时,不要在 context 布尔值中镜像状态(isLoading、isAuthenticated等)。
规则目录
- 状态图设计:0 状态图设计(编码前)
- 核心原则:1 机器创建模式、2 类型安全优先
- 设置对象规则:3 实现位置、4 参数类型
- 事件处理规则:5 使用 assertEvent 保证事件类型安全、6 事件对象要求
- 弃用模式预防:7 禁止使用的 v4 模式、8 现代 v5 等效写法
- 上下文和输入规则:9 上下文初始化、10 上下文更新
- 调用和 Actor 规则:11 调用配置、12 Actor 创建
- 类型辅助工具规则:13 类型辅助工具
- 测试规则:14 类型安全测试(14.1 确定性 Actor 测试、14.2 基于模型的测试、14.3 图工具)
- XState React 规则:15 通过 createActorContext 共享状态
- 最佳实践摘要:16 代码组织、17 性能考量、18 错误处理、19 文档
- 面向 AI Agent 的强制执行规则:20 强制模式、21 质量保证、22 文档含糊不清时
快速开始
最简类型化机器骨架:
import { setup, assign } from "xstate"
type Ctx = { count: number }
type Ev = { type: "inc" } | { type: "add"; amount: number } | { type: "reset" }
type Input = { initialCount?: number }
export const counterMachine = setup({
types: {
context: {} as Ctx,
events: {} as Ev,
input: {} as Input,
},
actions: {
inc: assign({ count: ({ context }) => context.count + 1 }),
add: assign({
count: ({ context }, params: { amount: number }) => context.count + params.amount,
}),
reset: assign({ count: ({ input }) => input.initialCount ?? 0 }),
},
}).createMachine({
id: "counter",
context: ({ input }) => ({ count: input.initialCount ?? 0 }),
on: {
inc: { actions: "inc" },
add: {
actions: {
type: "add",
params: ({ event }) => ({ amount: event.amount }),
},
},
reset: { actions: "reset" },
},
})
React 共享状态骨架:
import { createActorContext, shallowEqual } from "@xstate/react"
import { type SnapshotFrom } from "xstate"
import { counterMachine } from "./counterMachine"
const CounterCtx = createActorContext(counterMachine)
export const CounterProvider = CounterCtx.Provider
export const useCounterSelector = CounterCtx.useSelector
export const useCounterActorRef = CounterCtx.useActorRef
const selectCount = (s: SnapshotFrom<typeof counterMachine>) => s.context.count
export const useCount = () => useCounterSelector(selectCount)
const selectCtx = (s: SnapshotFrom<typeof counterMachine>) => s.context
export const useCounterContext = () => useCounterSelector(selectCtx, shallowEqual)
工作流(每次要做什么)
- 先阅读
references/xstate-v5-rules.md。 - 确定目标范围:
- 普通 machine/actor(非 React)
- React 集成(
createActorContext)或本地useActor - 从 v4 模式迁移
- 先设计状态图:
- 参见
references/xstate-v5-rules.md#0-statechart-design-before-you-code。 - 记录:状态清单、事件目录(负载 + 来源)、转换表、异步/actor 边界,以及验收测试场景。
- 类型设计前,先记录边界/拆分决策:
- 哪些关注点应放在独立 actor/machine 中(依据生命周期、所属方和故障模式)。
- 哪些关注点有意在顶层编排(以及原因)。
- 如果将逻辑保留在一个 machine 中,记录任何例外说明。
- 默认执行此步骤,除非用户明确要求跳过规划。
- 参见
- 先设计类型:
types.context、types.events和types.input(以及适用时的types.output)- 仅当代码库已使用 Zod 或用户要求时,才使用
zodschema。
- 将实现放入
setup({ actions, guards, actors }):- 需要事件派生数据时,actions/guards 使用带类型的
params。 - 仅在必要时于实现中读取事件专属字段;此时使用
assertEvent。
- 需要事件派生数据时,actions/guards 使用带类型的
- 选择异步边界:
- 请求/响应流程优先使用带类型
input的invoke。 - 对于长生命周期子 actor,使用
spawnChild/stopChild。
- 请求/响应流程优先使用带类型
- 使用 v5 运行时 API:
createActor(machine)(不要使用interpret)raise/sendTo(不要使用sendaction)enqueueActions(不要使用pure/choose)
- 验证:
tsc通过;params中没有泄漏的any。- 不发送字符串事件。
- 没有 v4 禁用的导入或配置键(
cond、withContext、withConfig等)。 - 异步 actor 测试优先使用
waitFor;基于模型/图的测试生成优先使用xstate/graph(createTestModel、getShortestPaths等)。
何时可以接受单体结构
仅当单个顶层 machine 是用于协调子 actor 或紧密耦合的并行应用外壳关注点的轻量编排根节点时,才使用它。即便如此,无关的领域逻辑也应放在独立的 actor/machine 中,并通过显式事件通信。
审查清单(审查 PR 时使用)
- 所有 machine 均使用
setup(...).createMachine(...)。 - Machine 有清晰的状态图规划(状态/事件/转换表),或 PR 描述中包含该规划。
- 没有领域设计异味:一个 machine 只承担一项职责,或明确的编排范围。
- 没有缺乏书面理由的状态镜像 context 标志。
types中没有实现。actions/guards仅通过params或assertEvent读取事件字段。- 没有
interpret、Machine、withConfig、withContext、cond、send、pure、choose。 invoke有onError(并在适当时有onDone)。- React 用法优先在模块作用域使用
createActorContext;selector 保持稳定,对象使用shallowEqual。
常见修复
-
在 action 中使用事件负载:
- 优先做法:在
params中计算负载,并让 action 实现依赖params。 - 否则:在实现中添加
assertEvent(event, 'someType')。
- 优先做法:在
-
v4
cond:- 重命名为
guard,并将 guard 实现移入setup({ guards: { ... } })。
- 重命名为
-
v4
interpret:- 替换为
createActor(machine),并在需要时调用.start()。
- 替换为
备注
如果官方文档含糊不清,请遵循规则文件中的指引:通过查阅 XState v5 源码、问题/讨论,或编写最小复现/类型测试来确认行为,而不是猜测。
评论
支持 Markdown