mcpField
R
react-state-machine

react-state-machine

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

关于 react-state-machine

此技能为 React 开发者提供使用 XState v5 状态机与 Actor 模型组织 UI 行为的指导。它涵盖状态设计、React 集成、异步流程及测试等模式。

主要功能

  1. 01显式建模状态与转换
  2. 02使用 Actor 管理异步流程
  3. 03提供 React Hook 集成指导
  4. 04状态机测试与错误处理模式
  5. 05性能选择器与拆分建议

适用场景

  1. 01整理加载、成功和错误状态
  2. 02协调超时、延迟与防抖逻辑
  3. 03构建身份验证或文件上传流程
  4. 04测试并优化复杂 React 状态

安装

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

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

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

包含的文件

除 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