关于 react-state-machine
此技能为 React 开发者提供使用 XState v5 状态机与 Actor 模型组织 UI 行为的指导。它涵盖状态设计、React 集成、异步流程及测试等模式。
主要功能
- 01显式建模状态与转换
- 02使用 Actor 管理异步流程
- 03提供 React Hook 集成指导
- 04状态机测试与错误处理模式
- 05性能选择器与拆分建议
适用场景
- 01整理加载、成功和错误状态
- 02协调超时、延迟与防抖逻辑
- 03构建身份验证或文件上传流程
- 04测试并优化复杂 React 状态
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ bobmatnyc/ claude-mpm-skills --skill react-state-machine安装后在 Claude Code 中输入/react-state-machine确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.jsonreferences/composition-patterns.mddecision-trees.mderror-handling.mdmigration-guide.mdperformance.mdpersistence-hydration.mdreact-integration.mdreal-world-patterns.mdskills-architecture.mdtesting-patterns.mdxstate-v5-patterns.md详细介绍
使用 XState v5 实现 React 状态机
概述
状态机通过将 UI 行为建模为明确的状态、转换和事件,使不可能出现的状态无法表示。XState v5(npm 每周下载量超过 250 万次)将状态机与 Actor 模型融为一体——每个状态机都是具有独立生命周期的实体,因此能够实现复杂的组合模式。
何时使用此技能
触发模式:
- 布尔标志爆炸:出现多个
isLoading、isError、isSuccess标志 - 隐式状态:编写
if (isLoading && !isError && data)来推导模式 - 防御式编码:在更新状态前添加保护条件,以防止无效转换
- 时间协调:在不同状态间处理超时、延迟和防抖
- 状态依赖:一个状态依赖另一个状态才能正确更新
不适用于:
- 没有异步操作的简单布尔切换(
useState更简单) - 只包含基本验证的单个表单字段(
useReducer足以应对) - 服务端状态缓存(React Query/TanStack Query 可以处理)
- 静态数据转换(
useMemo更合适) - 简单计数器或切换(
useState更清晰)
请参阅 decision-trees.md,获取完整的决策指南
核心心智模型
有限状态表示行为模式:idle、loading、success、error。组件同一时间只能处于一个状态。
**上下文(扩展状态)**用于存储不会定义不同状态的量化数据。有限状态表示“正在播放”;上下文则表示播放的内容和音量。
事件触发状态之间的转换。事件是对象:{ type: 'SUBMIT', data: formData }。
守卫有条件地允许或阻止转换:{ guard: 'hasValidInput' }。
动作是在转换期间或进入/退出状态时触发的即发即弃副作用。
调用的 Actor是具有生命周期管理和清理机制的长时间运行进程(API 调用、订阅)。
快速入门:XState v5 setup() 模式
import { setup, assign, fromPromise } from 'xstate';
const fetchMachine = setup({
types: {
context: {} as { data: User | null; error: string | null },
events: {} as
| { type: 'FETCH'; userId: string }
| { type: 'RETRY' }
},
actors: {
fetchUser: fromPromise(async ({ input, signal }) => {
const res = await fetch(`/api/users/${input.userId}`, { signal });
if (!res.ok) throw new Error(res.statusText);
return res.json();
})
},
actions: {
setData: assign({ data: ({ event }) => event.output }),
setError: assign({ error: ({ event }) => event.error.message })
}
}).createMachine({
id: 'fetch',
initial: 'idle',
context: { data: null, error: null },
states: {
idle: { on: { FETCH: 'loading' } },
loading: {
invoke: {
src: 'fetchUser',
input: ({ event }) => ({ userId: event.userId }),
onDone: { target: 'success', actions: 'setData' },
onError: { target: 'failure', actions: 'setError' }
}
},
success: { on: { FETCH: 'loading' } },
failure: { on: { RETRY: 'loading' } }
}
});
React 集成决策树
| 使用场景 | Hook | 原因 |
|---|---|---|
| 简单的组件状态 | useMachine | 直接明了,在所有状态变化时重新渲染 |
| 对性能要求高 | useActorRef + useSelector | 仅触发选择性重新渲染 |
| 全局/共享状态 | createActorContext | 与 React Context 集成 |
基本模式:
import { useMachine } from '@xstate/react';
function Toggle() {
const [snapshot, send] = useMachine(toggleMachine);
return (
<button onClick={() => send({ type: 'TOGGLE' })}>
{snapshot.matches('inactive') ? 'Off' : 'On'}
</button>
);
}
性能模式:
import { useActorRef, useSelector } from '@xstate/react';
const selectCount = (s) => s.context.count;
const selectLoading = (s) => s.matches('loading');
function Counter() {
const actorRef = useActorRef(counterMachine);
const count = useSelector(actorRef, selectCount);
const loading = useSelector(actorRef, selectLoading);
// Only re-renders when count or loading changes
}
应避免的反模式
❌ 状态爆炸:将相互正交的关注点建模为扁平状态。请改用并行状态。
❌ 从动作中发送事件:切勿在 assign 中调用 send()。内部事件请使用 raise。
❌ 非纯守卫:守卫必须是纯函数,不得包含副作用或外部修改。
❌ 订阅整个状态:使用有针对性的选择器和 useSelector。
❌ 未对模型进行记忆化:
// WRONG
const model = Model.fromJson(layout); // New model every render
// CORRECT
const modelRef = useRef(Model.fromJson(layout));
参考资料导航
核心模式
- xstate-v5-patterns.md:完整的 v5 API、状态图(层级/并行/历史)、Promise Actor
- react-integration.md:useMachine 与 useActorRef 的比较、Context 模式、副作用处理
- testing-patterns.md:单元测试、Actor 模拟、可视化调试
决策与最佳实践
- decision-trees.md:何时使用状态机,而不是 useState/useReducer/React Query;状态机拆分策略
- real-world-patterns.md:完整示例——身份验证流程、文件上传、向导、撤销/重做、购物车
- error-handling.md:错误边界、重试策略、断路器、优雅降级
- performance.md:选择器记忆化、React.memo 集成、用于提升性能的状态机拆分
高级主题
- persistence-hydration.md:localStorage 持久化、SSR/Next.js 水合、快照序列化
- migration-guide.md:从 useState/useReducer 逐步迁移,并附有前后对比示例
- composition-patterns.md:Actor 通信、状态机组合、高阶状态机、systemId
- skills-architecture.md:输入/输出参数化、库结构
关键提醒
- setup() 是 v5 的方式:具备强大的 TypeScript 类型推断、actor 注册和 action 定义
- 异步操作用 invoke,同步操作用 action:action 是即发即忘的;被调用的 actor 具有生命周期
- 用有限状态表示模式,用 context 存储数据:不要为每种数据变化都创建状态
- 先进行可视化:Stately Studio(stately.ai/editor)能让状态机成为动态文档
警示信号
- 布尔标志超过 3-4 个 → 需要状态机
- 通过编写
if (a && !b && c)来判断模式 → 应明确表示状态 - 无效的状态组合导致错误 → 状态机可防止不可能的状态
- 无法向利益相关者解释状态转换 → 可视化可以解决这个问题
相关技能
- react:React 模式的上级技能
- nextjs:服务端/客户端状态协调
- test-driven-development:在集成 UI 之前,使用 createActor 测试状态机