mcpField
X
xstate-v5

xstate-v5

github.com/johnlombardo-dev开发142 次安装37 周前更新
代码仓库

关于 xstate-v5

xstate-v5 是一项严格的 Codex 技能,用于设计、实现、重构和审查 TypeScript 与 React 中的类型化 XState v5 状态机。它要求先规划状态图,并规范 Actor 边界、事件类型以及对 v4 旧模式的规避。

主要功能

  1. 01先设计状态图及相关文档
  2. 02严格遵循 XState v5 API 规范
  3. 03类型化事件、上下文与输入
  4. 04明确 invoke 和子 Actor 边界
  5. 05React Actor 上下文使用指引

适用场景

  1. 01规划新的 XState v5 状态机
  2. 02重构或审查 XState v5 代码
  3. 03迁移 XState v4 旧模式
  4. 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 等)。

规则目录

快速开始

最简类型化机器骨架:

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)

工作流(每次要做什么)

  1. 先阅读 references/xstate-v5-rules.md。
  2. 确定目标范围:
    • 普通 machine/actor(非 React)
    • React 集成(createActorContext)或本地 useActor
    • 从 v4 模式迁移
  3. 先设计状态图:
    • 参见 references/xstate-v5-rules.md#0-statechart-design-before-you-code。
    • 记录:状态清单、事件目录(负载 + 来源)、转换表、异步/actor 边界,以及验收测试场景。
    • 类型设计前,先记录边界/拆分决策:
      • 哪些关注点应放在独立 actor/machine 中(依据生命周期、所属方和故障模式)。
      • 哪些关注点有意在顶层编排(以及原因)。
      • 如果将逻辑保留在一个 machine 中,记录任何例外说明。
    • 默认执行此步骤,除非用户明确要求跳过规划。
  4. 先设计类型:
    • types.context、types.events 和 types.input(以及适用时的 types.output)
    • 仅当代码库已使用 Zod 或用户要求时,才使用 zod schema。
  5. 将实现放入 setup({ actions, guards, actors }):
    • 需要事件派生数据时,actions/guards 使用带类型的 params。
    • 仅在必要时于实现中读取事件专属字段;此时使用 assertEvent。
  6. 选择异步边界:
    • 请求/响应流程优先使用带类型 input 的 invoke。
    • 对于长生命周期子 actor,使用 spawnChild/stopChild。
  7. 使用 v5 运行时 API:
    • createActor(machine)(不要使用 interpret)
    • raise / sendTo(不要使用 send action)
    • enqueueActions(不要使用 pure/choose)
  8. 验证:
    • 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