mcpField
R
react-state-machines

react-state-machines

github.com/bobmatnyc开发78158 次安装15 周前更新
代码仓库

关于 react-state-machines

面向 React 开发者的 XState v5 状态管理指导技能,涵盖状态机建模、Actor 与 React 集成及常见问题规避。

主要功能

  1. 01状态机概念与模式
  2. 02XState v5 setup 和 TypeScript 指导
  3. 03React Hook 选择与集成
  4. 04Actor 组合与异步流程
  5. 05测试、持久化与性能指导

适用场景

  1. 01用明确状态替代相互依赖的布尔标记
  2. 02协调异步 UI 流程与重试
  3. 03选择 useMachine 或 Actor 选择器
  4. 04迁移现有 useState 或 useReducer 逻辑

安装

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

$npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill react-state-machines

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

包含的文件

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

metadata.json
references/
composition-patterns.md
decision-trees.md
error-handling.md
migration-guide.md
performance.md
persistence-hydration.md
react-integration.md
real-world-patterns.md
skills-architecture.md
testing-patterns.md
xstate-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:输入/输出参数化、库结构

关键提醒

  1. setup() 是 v5 的方式:具备强大的 TypeScript 类型推断、actor 注册和 action 定义
  2. 异步操作用 invoke,同步操作用 action:action 是即发即忘的;被调用的 actor 具有生命周期
  3. 用有限状态表示模式,用 context 存储数据:不要为每种数据变化都创建状态
  4. 先进行可视化:Stately Studio(stately.ai/editor)能让状态机成为动态文档

警示信号

  • 布尔标志超过 3-4 个 → 需要状态机
  • 通过编写 if (a && !b && c) 来判断模式 → 应明确表示状态
  • 无效的状态组合导致错误 → 状态机可防止不可能的状态
  • 无法向利益相关者解释状态转换 → 可视化可以解决这个问题

相关技能

  • react:React 模式的上级技能
  • nextjs:服务端/客户端状态协调
  • test-driven-development:在集成 UI 之前,使用 createActor 测试状态机

评论

支持 Markdown