react-state-machines by bobmatnyc/claude-mpm-skills
npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill react-state-machines状态机通过将 UI 行为建模为明确的状态、转换和事件,使得不可能的状态无法表示。XState v5(每周 npm 下载量超过 250 万)将状态机与参与者模型统一起来——每个状态机都是一个具有自身生命周期的独立实体,支持复杂的组合模式。
触发模式:
isLoading、isError、isSuccess 标志if (isLoading && !isError && data) 来推导模式不适用于:
有关全面的决策指导,请参阅 decision-trees.md
有限状态 表示行为模式:、、、。一个组件一次只能处于一种状态。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
idleloadingsuccesserror上下文(扩展状态) 存储不定义不同状态的定量数据。有限状态说明“正在播放”;上下文说明 播放什么 以及 音量多大。
事件 触发状态之间的转换。事件是对象:{ type: 'SUBMIT', data: formData }。
守卫 有条件地允许/阻止转换:{ guard: 'hasValidInput' }。
动作 是在转换或状态进入/退出期间触发即忘的副作用。
调用的参与者 是具有生命周期管理和清理功能的长运行进程(API 调用、订阅)。
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' } }
}
});
| 使用场景 | 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);
// 仅在 count 或 loading 更改时重新渲染
}
❌ 状态爆炸:为不相关的关注点使用扁平状态。应改用并行状态。
❌ 从动作中发送事件:切勿在 assign 内部使用 send()。对于内部事件,请使用 raise。
❌ 不纯的守卫:守卫必须是纯函数——无副作用,无外部突变。
❌ 订阅整个状态:使用带有 useSelector 的聚焦选择器。
❌ 未记忆化模型:
// 错误
const model = Model.fromJson(layout); // 每次渲染都创建新模型
// 正确
const modelRef = useRef(Model.fromJson(layout));
if (a && !b && c) 来确定模式 → 状态应该是明确的每周安装次数
75
代码仓库
GitHub 星标数
18
首次出现
Jan 23, 2026
安全审计
安装于
claude-code57
opencode56
gemini-cli55
codex54
github-copilot50
cursor49
State machines make impossible states unrepresentable by modeling UI behavior as explicit states, transitions, and events. XState v5 (2.5M+ weekly npm downloads) unifies state machines with the actor model—every machine is an independent entity with its own lifecycle, enabling sophisticated composition patterns.
Trigger patterns:
isLoading, isError, isSuccess flagsif (isLoading && !isError && data) to derive modeDo not use for:
See decision-trees.md for comprehensive decision guidance
Finite states represent modes of behavior: idle, loading, success, error. A component can only be in ONE state at a time.
Context (extended state) stores quantitative data that doesn't define distinct states. The finite state says "playing"; context says what at what volume.
Events trigger transitions between states. Events are objects: { type: 'SUBMIT', data: formData }.
Guards conditionally allow/block transitions: { guard: 'hasValidInput' }.
Actions are fire-and-forget side effects during transitions or state entry/exit.
Invoked actors are long-running processes (API calls, subscriptions) with lifecycle management and cleanup.
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' } }
}
});
| Use Case | Hook | Why |
|---|---|---|
| Simple component state | useMachine | Straightforward, re-renders on all changes |
| Performance-critical | useActorRef + useSelector | Selective re-renders only |
| Global/shared state | createActorContext | React Context integration |
Basic pattern:
import { useMachine } from '@xstate/react';
function Toggle() {
const [snapshot, send] = useMachine(toggleMachine);
return (
<button onClick={() => send({ type: 'TOGGLE' })}>
{snapshot.matches('inactive') ? 'Off' : 'On'}
</button>
);
}
Performance pattern:
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
}
❌ State explosion : Flat states for orthogonal concerns. Use parallel states instead.
❌ Sending events from actions : Never send() inside assign. Use raise for internal events.
❌ Impure guards : Guards must be pure—no side effects, no external mutations.
❌ Subscribing to entire state : Use focused selectors with useSelector.
❌ Not memoizing model :
// WRONG
const model = Model.fromJson(layout); // New model every render
// CORRECT
const modelRef = useRef(Model.fromJson(layout));
if (a && !b && c) to determine mode → States should be explicitWeekly Installs
75
Repository
GitHub Stars
18
First Seen
Jan 23, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
claude-code57
opencode56
gemini-cli55
codex54
github-copilot50
cursor49
TanStack Query v5 完全指南:React 数据管理、乐观更新、离线支持
2,500 周安装