xstate-v5

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

XState v5

XState v5

Use this skill for state machine and statechart engineering first and API correctness second.
This skill is v5-only. When examples, blog posts, answers, or local code smell v4-ish, translate them rather than mixing versions. Prefer local repo code and official v5 docs over generic memory.
Your job:
  • choose between
    xstate
    and
    @xstate/store
  • design a sound machine or actor system from messy requirements
  • write modern XState v5 TypeScript code in a consistent style
  • review, repair, or improve existing XState code
  • migrate legacy v4-ish patterns when they appear
  • choose the right actor kind and action shape when the problem is not just
    assign(...)
    plus
    fromPromise(...)
  • connect machines and actors to
    @xstate/react
    ,
    @xstate/vue
    ,
    @xstate/svelte
    , or
    @xstate/solid
    when needed
本技能优先关注状态机与状态图工程实现,其次是API正确性。
本技能仅适用于v5版本。当遇到示例、博客文章、答案或本地代码带有v4风格时,请将其转换为v5版本,而非混合不同版本。优先参考本地仓库代码与官方v5文档,而非通用记忆内容。
你的工作内容:
  • xstate
    @xstate/store
    之间做出选择
  • 根据模糊的需求设计合理的状态机或actor系统
  • 以一致风格编写现代XState v5 TypeScript代码
  • 评审、修复或优化现有XState代码
  • 迁移出现的旧版v4风格模式
  • 当问题不只是
    assign(...)
    fromPromise(...)
    时,选择合适的actor类型与动作形态
  • 必要时将状态机与actors连接至
    @xstate/react
    @xstate/vue
    @xstate/svelte
    @xstate/solid

First pass

初步步骤

In an existing codebase:
  1. Inspect local
    package.json
    files and imports.
  2. Read nearby machines, stores, actors, and adapter usage.
  3. Distinguish between new code, local edits, and migration work before choosing how opinionated to be.
  4. Preserve surrounding style by default.
在现有代码库中:
  1. 检查本地
    package.json
    文件与导入语句。
  2. 阅读附近的状态机、store、actors与适配器用法。
  3. 在选择要遵循的风格前,区分新代码、本地编辑代码与迁移工作。
  4. 默认保留现有代码风格。

Task mode

任务模式

Choose a mode before writing code:
  • New code: prefer the modern v5 patterns in this skill.
  • Local edit: preserve local structure and naming unless the current code is mixed, broken, or cleanup was requested.
  • Migration: prefer the smallest safe translation to v5. Preserve structure and semantics unless broader normalization was requested.
For migration and local edits, do not introduce
setup(...)
, named implementations, object-form guards/actions, tags, or actor decomposition unless they are required for correctness, significantly reduce local complexity, or were explicitly requested.
编写代码前选择一种模式:
  • 新代码:优先使用本技能中的现代v5模式。
  • 本地编辑:保留本地代码结构与命名,除非当前代码存在混合版本、错误或需要清理。
  • 迁移:优先采用最小化安全转换至v5版本。保留原有结构与语义,除非要求进行更广泛的标准化。
对于迁移与本地编辑任务,除非为了正确性、显著降低本地复杂度或被明确要求,否则不要引入
setup(...)
、命名实现、对象形式的守卫/动作、标签或actor分解。

Choose the tool

工具选择

Prefer
@xstate/store
when the domain is simple event-based state management:
  • no meaningful finite modes that need coordination, orchestration, or explicit lifecycle modeling
  • no invoked async processes
  • no actor communication
  • no need for state machine or statechart concepts such as guarded transitions, delayed transitions, parallel states, or history
Simple fetching or mutation logic can still fit
@xstate/store
if it does not need machine-level orchestration.
Prefer XState when the domain has one or more of these:
  • finite modes that matter to behavior or UI
  • async workflows, retries, cancellation, or background processes
  • multiple interacting processes or child actors
  • explicit guards, delays, tags, or transition rules
  • a need to model business process flow, not just update state
If the problem is too simple for a machine, say so plainly and recommend
@xstate/store
.
当领域为简单的基于事件的状态管理时,优先选择
@xstate/store
  • 无需协调、编排或显式生命周期建模的有限状态模式
  • 无异步调用流程
  • 无actor通信
  • 无需状态机或状态图概念,如守卫转换、延迟转换、并行状态或历史状态
简单的获取或变更逻辑若无需状态机级别的编排,仍可使用
@xstate/store
当领域存在以下一种或多种情况时,优先选择XState:
  • 对行为或UI有影响的有限状态模式
  • 异步工作流、重试、取消或后台进程
  • 多个交互进程或子actors
  • 显式的守卫、延迟、标签或转换规则
  • 需要建模业务流程而非仅更新状态
若问题过于简单无需使用状态机,请明确说明并推荐
@xstate/store

Workflow

工作流程

When requirements are fuzzy:
  1. Sketch the machine shape first.
  2. Name the important states, events, context, actors, and tags.
  3. Call out uncertainty or tradeoffs briefly.
  4. Then write the code.
When requirements are already clear, keep the sketch short or implicit and move to code.
Do not over-refactor existing code for conceptual purity. Improve the model where it matters, but preserve working structure when a larger rewrite is not justified.
当需求模糊时:
  1. 先绘制状态机草图。
  2. 命名重要的状态、事件、上下文、actors与标签。
  3. 简要说明不确定性或权衡点。
  4. 然后编写代码。
当需求明确时,可简化草图或省略,直接编写代码。
不要为了概念纯粹性过度重构现有代码。在关键处优化模型,但当大规模重写不合理时,保留可运行的结构。

Modeling questions

建模问题

Use these questions before writing or revising code:
  • What are the finite states? Put modes in states, not in context.
  • What belongs in context? Keep only durable data, not derivable booleans or duplicated mode.
  • What are the domain events? Prefer meaningful names, often dot-separated, such as
    form.submitted
    or
    order.confirmed
    .
  • What should be a guard versus a separate branch state?
  • What side effects should be invoked actors versus transition actions?
  • Can an event carry the needed data instead of stashing temporary relay data in context for a later state?
  • Is
    fromPromise(...)
    actually the right actor, or is this a callback/subscription protocol that should send events back over time?
  • Should the machine emit events to the surrounding system instead of forcing consumers to infer everything from context?
  • Should a child concern be a spawned/invoked actor instead of more parent complexity?
  • Does the parent really need these extra intermediate states, or should a child actor own that internal detail?
  • Which states need tags for UI semantics such as
    'loading'
    ,
    'error'
    , or
    'dirty'
    ?
  • Can the UI use
    snapshot.matches(...)
    , tags, or
    snapshot.can(...)
    instead of extra booleans?
编写或修改代码前思考以下问题:
  • 有哪些有限状态?将模式放在状态中,而非上下文里。
  • 哪些内容属于上下文?仅保留持久化数据,而非可推导的布尔值或重复的模式。
  • 有哪些领域事件?优先使用有意义的名称,通常以点分隔,如
    form.submitted
    order.confirmed
  • 哪些内容应该作为守卫,哪些应该作为单独的分支状态?
  • 哪些副作用应该作为调用的actors,哪些应该作为转换动作?
  • 事件能否携带所需数据,而非将临时中继数据存储在上下文中供后续状态使用?
  • fromPromise(...)
    是否真的是合适的actor,还是这是一个需要随时间发送事件回来的回调/订阅协议?
  • 状态机是否应该向周边系统发送事件,而非强制消费者从上下文中推断所有信息?
  • 子关注点是否应该作为生成/调用的actors,而非增加父级复杂度?
  • 父级是否真的需要这些额外的中间状态,还是应该让子actor处理这些内部细节?
  • 哪些状态需要用于UI语义的标签,如
    'loading'
    'error'
    'dirty'
  • UI能否使用
    snapshot.matches(...)
    、标签或
    snapshot.can(...)
    替代额外的布尔值?

Preferred v5 patterns

推荐的v5模式

For new code, prefer these patterns unless the local codebase has a strong reason not to:
  • TypeScript only.
  • Prefer
    setup({...}).createMachine({...})
    .
  • Define named
    actions
    ,
    guards
    ,
    actors
    , and
    delays
    in
    setup(...)
    .
  • Prefer transition objects like
    { target: 'next' }
    over shorthand when it improves consistency.
  • Prefer arrays for
    actions
    , even for a single action, when that keeps the shape consistent.
  • Prefer action and guard objects such as
    { type: 'track' }
    or
    { type: 'isValid', params: ... }
    when the intent is reusable or named.
  • Prefer
    tags: []
    for UI semantics and cross-cutting state meaning.
  • Prefer domain-oriented event names; preserve local naming conventions if they are already established.
  • Prefer plain functions for derived values instead of storing derivable data in context.
  • Prefer event payloads over temporary context relay when one state only needs to pass data to the next step.
  • Prefer passing data into named actions and guards via
    params
    instead of reaching into
    event
    directly.
  • Do not treat
    assign(...)
    as the only action pattern. For typed reusable logic beyond simple context updates, consider setup-scoped helpers from a
    const machineSetup = setup(...)
    object, such as
    machineSetup.createAction(...)
    ,
    machineSetup.enqueueActions(...)
    , and
    machineSetup.emit(...)
    , or named action objects when they fit the behavior better.
  • When a named
    assign(...)
    action updates multiple context properties, every property updater shares the same
    params
    type. Use one coherent params object for all updated fields, or split the work into separate named actions. Do not mix incompatible params shapes inside one assigner.
  • When an implementation must inspect
    event
    and the type is not obvious, prefer
    assertEvent(...)
    for narrowing.
  • Avoid
    as any
    and other loose casts in examples and final code unless there is no cleaner local option.
  • Choose actor logic intentionally: prefer
    fromPromise(...)
    for one request/one result, and prefer
    fromCallback(...)
    for subscriptions, timers, external callbacks, and multi-event protocols that send events back over time.
  • Consider
    emit(...)
    when the machine should notify the surrounding system directly.
  • Keep parent states coarse when possible. If extra intermediate states mainly model an implementation detail, prefer letting a child actor own that internal behavior.
  • If you show multiple files, wire them completely. Include real imports/exports for referenced modules and components, or keep the example smaller.
Keep event naming consistent. The
.
in event names is useful and meaningful, including for partial event descriptors and clearer domain grouping. See the official docs on events and transitions and TypeScript narrowing with
assertEvent(...)
.
See
references/examples.md
for canonical code shapes,
references/advanced-patterns.md
when the task involves typed actions beyond
assign(...)
, callback actors, emitted events, or persistence/hydration, and
references/observables-and-inspection.md
for
fromObservable(...)
, inspection, and browser inspector patterns.
对于新代码,除非本地代码库有充分理由不使用,否则优先采用以下模式:
  • 仅使用TypeScript。
  • 优先使用
    setup({...}).createMachine({...})
  • setup(...)
    中定义命名的
    actions
    guards
    actors
    delays
  • 当提升一致性时,优先使用如
    { target: 'next' }
    的转换对象而非简写形式。
  • 为保持形态一致,即使单个动作也优先使用数组形式的
    actions
  • 当意图是可复用或命名时,优先使用动作与守卫对象,如
    { type: 'track' }
    { type: 'isValid', params: ... }
  • 优先使用
    tags: []
    用于UI语义与跨领域状态含义。
  • 优先使用面向领域的事件名称;若已有本地命名约定则保留。
  • 优先使用纯函数处理派生值,而非将可推导数据存储在上下文中。
  • 当一个状态仅需向下一步传递数据时,优先使用事件负载而非临时上下文中继。
  • 优先通过
    params
    将数据传递给命名动作与守卫,而非直接访问
    event
  • 不要将
    assign(...)
    视为唯一的动作模式。对于超出简单上下文更新的类型化可复用逻辑,考虑来自
    const machineSetup = setup(...)
    对象的作用域内助手,如
    machineSetup.createAction(...)
    machineSetup.enqueueActions(...)
    machineSetup.emit(...)
    ,或符合行为需求的命名动作对象。
  • 当命名的
    assign(...)
    动作更新多个上下文属性时,所有属性更新器共享相同的
    params
    类型。为所有更新字段使用一个连贯的params对象,或拆分为多个命名动作。不要在一个赋值器中混合不兼容的params形态。
  • 当实现必须检查
    event
    且类型不明确时,优先使用
    assertEvent(...)
    进行类型收窄。
  • 除非没有更清晰的本地选项,否则在示例与最终代码中避免使用
    as any
    及其他松散类型转换。
  • 有意选择actor逻辑:优先使用
    fromPromise(...)
    处理单次请求/单次结果的场景,优先使用
    fromCallback(...)
    处理订阅、定时器、外部回调及随时间发送事件回来的多事件协议。
  • 当状态机需要直接通知周边系统时,考虑使用
    emit(...)
  • 尽可能保持父状态简洁。若额外的中间状态主要用于建模实现细节,优先让子actor处理该内部行为。
  • 若展示多个文件,请完整连接它们。包含引用模块与组件的真实导入/导出语句,或简化示例。
保持事件命名一致。事件名称中的
.
是有用且有意义的,包括用于部分事件描述符与更清晰的领域分组。请查看官方文档中的事件与转换使用
assertEvent(...)
进行TypeScript类型收窄
如需标准代码形态,请查看
references/examples.md
;当任务涉及
assign(...)
之外的类型化动作、回调actors、发送事件或持久化/ hydration时,请查看
references/advanced-patterns.md
;如需
fromObservable(...)
、检查与浏览器检查器模式,请查看
references/observables-and-inspection.md

UI integration

UI集成

Prefer letting the machine drive UI behavior:
  • use
    snapshot.matches(...)
    for finite mode checks
  • use tags for semantic checks such as loading, saving, or dirty
  • use
    snapshot.can(...)
    to drive whether an event is currently valid
  • avoid parallel piles of manual booleans that restate machine truth
If a component owns a small local actor,
useMachine(...)
is often enough.
If an actor is shared, long-lived, or performance-sensitive:
  • create or obtain an actor ref once
  • read slices with
    useSelector(...)
  • use actor context/provider helpers when the actor is shared across a subtree
See
references/adapters.md
for concise adapter guidance.
优先让状态机驱动UI行为:
  • 使用
    snapshot.matches(...)
    进行有限模式检查
  • 使用标签进行语义检查,如加载、保存或脏状态
  • 使用
    snapshot.can(...)
    判断事件当前是否有效
  • 避免手动维护一堆重复状态机逻辑的布尔值
若组件拥有小型本地actor,
useMachine(...)
通常足够。
若actor是共享、长期运行或对性能敏感的:
  • 一次性创建或获取actor引用
  • 使用
    useSelector(...)
    读取切片数据
  • 当actor在子树中共享时,使用actor上下文/提供者助手
如需简洁的适配器指南,请查看
references/adapters.md

Review and repair guidance

评审与修复指南

When reviewing or fixing XState code, look for:
  • finite mode stored in context instead of states
  • giant machines that should be split into child actors
  • side effects hidden in random callbacks or mixed into assigners
  • v4 and v5 concepts mixed together in the same machine
  • state names and event names that describe UI mechanics instead of domain meaning
  • extra booleans that should be replaced by
    matches(...)
    , tags, or selectors
  • missed opportunities to use
    @xstate/store
    when the problem is simple
  • named
    assign(...)
    actions whose property updaters implicitly expect different
    params
    shapes
Avoid pushing decomposition too far. Actor boundaries are useful when they improve clarity, ownership, or concurrency. Do not split for its own sake.
评审或修复XState代码时,需注意:
  • 有限模式存储在上下文中而非状态中
  • 应拆分为子actors的大型状态机
  • 隐藏在随机回调中或混入赋值器的副作用
  • 同一状态机中混合v4与v5概念
  • 描述UI机制而非领域含义的状态名称与事件名称
  • 应被
    matches(...)
    、标签或选择器替代的额外布尔值
  • 问题简单却未使用
    @xstate/store
    的情况
  • 属性更新器隐式期望不同
    params
    形态的命名
    assign(...)
    动作
避免过度拆分。当actor边界提升清晰度、归属权或并发性时才有用,不要为了拆分而拆分。

Migration

迁移

When legacy code is present, translate toward v5 gradually and locally unless the user asked for a broader migration.
Common migration targets include:
  • cond
    ->
    guard
  • schema
    ->
    types
  • services
    ->
    actors
  • interpret(...)
    ->
    createActor(...)
  • old function signatures -> destructured v5 arguments such as
    ({ context, event })
For migration tasks:
  • preserve local structure first
  • change only what is needed for correctness, compatibility, or clarity
  • do not normalize into the full preferred house style unless the user asked for that
  • explain any non-local refactor you choose to make
  • do not preserve dead or invalid hook option patterns just because they existed nearby
  • when migrating React usage, prefer valid current hook surfaces over trying to smuggle old
    interpret
    -style implementation overrides into
    useMachine(...)
Use
references/v4-to-v5-quick-ref.md
for quick translation patterns.
当存在遗留代码时,除非用户要求更广泛的迁移,否则逐步、局部地转换至v5版本。
常见迁移目标包括:
  • cond
    ->
    guard
  • schema
    ->
    types
  • services
    ->
    actors
  • interpret(...)
    ->
    createActor(...)
  • 旧函数签名 -> 解构后的v5参数,如
    ({ context, event })
对于迁移任务:
  • 优先保留本地结构
  • 仅修改为正确性、兼容性或清晰度所需的内容
  • 除非用户要求,否则不要标准化为完整的推荐风格
  • 解释任何非局部的重构操作
  • 不要保留无效的钩子选项模式,即使它们之前存在
  • 迁移React用法时,优先使用当前有效的钩子接口,而非尝试将旧的
    interpret
    风格实现覆盖混入
    useMachine(...)
如需快速转换模式,请查看
references/v4-to-v5-quick-ref.md

Persistence

持久化

When persistence or hydration matters:
  • persist snapshots, not just context, unless context-only restore is truly sufficient
  • prefer
    actor.getPersistedSnapshot()
    for saving machine state
  • prefer hydrating with
    createActor(machine, { snapshot })
    or adapter/provider snapshot options where available
  • do not hand-roll restoration by guessing the state value plus a partial context blob
See
references/advanced-patterns.md
for concrete persistence and hydration patterns.
当持久化或hydration很重要时:
  • 持久化快照而非仅上下文,除非仅恢复上下文就足够
  • 优先使用
    actor.getPersistedSnapshot()
    保存状态机状态
  • 优先使用
    createActor(machine, { snapshot })
    或适配器/提供者的快照选项进行hydration
  • 不要通过猜测状态值加部分上下文数据来手动实现恢复
如需具体的持久化与hydration模式,请查看
references/advanced-patterns.md

Testing

测试

Do not introduce testing guidance unless the user asks about testing or requests tests.
If they do ask, keep it brief:
  • test key transitions and guards
  • test happy path and failure path actor behavior
  • test the UI against machine state rather than duplicating stateful logic
  • mention model-based testing utilities when useful
If a link would help, point them to the XState testing and graph/path generation docs.
除非用户询问测试或要求编写测试,否则不要提供测试指导。
若用户询问,请保持简洁:
  • 测试关键转换与守卫
  • 测试actor行为的成功路径与失败路径
  • 根据状态机状态测试UI,而非重复实现有状态逻辑
  • 必要时提及基于模型的测试工具
如需链接,请指向XState的测试图/路径生成文档。

Optional tools

可选工具

Stately Studio and inspection tools can help with design, debugging, and communication, but they are optional. Mention them when the user is designing a machine, wants visualization, or needs better debugging visibility.
Stately Studio与检查工具可帮助设计、调试与沟通,但为可选工具。当用户设计状态机、需要可视化或更好的调试可见性时提及它们。

Output format

输出格式

Prefer this output shape:
  1. Short machine sketch if requirements are fuzzy.
  2. Code.
  3. Brief rationale explaining states, events, context, actors, and tags.
  4. Migration notes only if relevant.
Prefer fewer complete files over a larger but partially wired example. Do not sketch extra shell files unless they are fully wired.
See
references/examples.md
for more canonical examples that can grow over time.
优先采用以下输出形态:
  1. 若需求模糊,提供简短的状态机草图。
  2. 代码。
  3. 简要说明状态、事件、上下文、actors与标签的设计理由。
  4. 仅在相关时提供迁移说明。
优先提供少量完整文件,而非更大但部分未连接的示例。除非完全连接,否则不要绘制额外的外壳文件。
如需更多标准示例,请查看
references/examples.md
,示例可随时间扩展。

Final self-check

最终自检

Before you answer, do a quick compile-minded pass:
  • every JSX component from another file has a real import
  • every imported symbol is actually exported by the shown file
  • every
    send(...)
    and
    snapshot.can(...)
    call uses real event objects
  • async work uses
    actors
    /
    fromPromise
    and
    invoke.input
    , not legacy shapes
  • if you reference an external helper from the prompt or surrounding system, declare its signature or show enough typed shape for the example to stand on its own
  • migration examples do not pass invalid implementation override objects into hooks just to preserve dead local code
  • no placeholder shell files, omitted imports,
    ...
    , or pseudocode in code fences that are meant to be runnable
If a complete multi-file answer is getting bulky, remove the shell file and keep the smaller set of files that still demonstrates the pattern correctly.
回答前,快速进行一次编译层面的检查:
  • 来自其他文件的每个JSX组件都有真实的导入语句
  • 每个导入的符号都确实由所示文件导出
  • 每个
    send(...)
    snapshot.can(...)
    调用都使用真实的事件对象
  • 异步工作使用
    actors
    /
    fromPromise
    invoke.input
    ,而非旧形态
  • 若引用提示或周边系统中的外部助手,请声明其签名或展示足够的类型化形态以使示例独立运行
  • 迁移示例不要将无效的实现覆盖对象传入钩子以保留无用的本地代码
  • 在可运行的代码块中不要使用占位符外壳文件、省略的导入、
    ...
    或伪代码
若完整的多文件回答过于冗长,移除外壳文件,保留仍能正确展示模式的较小文件集。