react-state-machines
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact State Machines with XState v5
基于XState v5的React状态机
Overview
概述
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.
状态机通过将UI行为建模为明确的状态、转换和事件,使不可能的状态无法被表示。XState v5(每周npm下载量超过250万次)将状态机与Actor模型相结合——每个机器都是具有独立生命周期的独立实体,支持复杂的组合模式。
When to Use This Skill
何时使用该技能
Trigger patterns:
- Boolean flag explosion: multiple ,
isLoading,isErrorflagsisSuccess - Implicit states: writing to derive mode
if (isLoading && !isError && data) - Defensive coding: guards before state updates to prevent invalid transitions
- Timing coordination: timeouts, delays, debouncing across states
- State dependencies: one state depends on another to update correctly
Do not use for:
- Simple boolean toggles with no async (useState is simpler)
- Single form fields with basic validation (useReducer suffices)
- Server state caching (React Query/TanStack Query handles this)
- Static data transformations (useMemo is better)
- Simple counters or toggles (useState is clearer)
See decision-trees.md for comprehensive decision guidance
触发场景:
- 布尔标志泛滥:多个、
isLoading、isError标志isSuccess - 隐式状态:通过编写来推导模式
if (isLoading && !isError && data) - 防御性编码:在状态更新前添加守卫以防止无效转换
- 时序协调:跨状态的超时、延迟、防抖处理
- 状态依赖:一个状态依赖另一个状态才能正确更新
不适用场景:
- 无异步逻辑的简单布尔切换(使用useState更简单)
- 基础验证的单个表单字段(useReducer足够)
- 服务器状态缓存(React Query/TanStack Query可处理此场景)
- 静态数据转换(useMemo更合适)
- 简单计数器或切换器(useState更清晰)
详见decision-trees.md获取全面的决策指导
Core Mental Model
核心思维模型
Finite states represent modes of behavior: , , , . A component can only be in ONE state at a time.
idleloadingsuccesserrorContext (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.
有限状态代表行为模式:、、、。组件同一时间只能处于一种状态。
idleloadingsuccesserror上下文(扩展状态)存储不定义不同状态的定量数据。有限状态表示“正在播放”;上下文则说明播放的内容和音量。
事件触发状态之间的转换。事件是对象:。
{ type: 'SUBMIT', data: formData }守卫有条件地允许/阻止转换:。
{ guard: 'hasValidInput' }动作是转换期间或状态进入/退出时的一次性副作用。
调用的Actor是具有生命周期管理和清理机制的长期运行进程(API调用、订阅)。
Quick Start: XState v5 setup() Pattern
快速开始:XState v5 setup() 模式
typescript
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' } }
}
});typescript
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 Integration Decision Tree
React集成决策树
| Use Case | Hook | Why |
|---|---|---|
| Simple component state | | Straightforward, re-renders on all changes |
| Performance-critical | | Selective re-renders only |
| Global/shared state | | React Context integration |
Basic pattern:
typescript
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:
typescript
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
}| 使用场景 | Hook | 原因 |
|---|---|---|
| 简单组件状态 | | 直观易懂,所有变化都会触发重渲染 |
| 性能敏感场景 | | 仅在指定状态变化时触发重渲染 |
| 全局/共享状态 | | 与React Context集成 |
基础模式:
typescript
import { useMachine } from '@xstate/react';
function Toggle() {
const [snapshot, send] = useMachine(toggleMachine);
return (
<button onClick={() => send({ type: 'TOGGLE' })}>
{snapshot.matches('inactive') ? 'Off' : 'On'}
</button>
);
}性能优化模式:
typescript
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变化时触发重渲染
}Anti-Patterns to Avoid
需避免的反模式
❌ State explosion: Flat states for orthogonal concerns. Use parallel states instead.
❌ Sending events from actions: Never inside . Use for internal events.
send()assignraise❌ Impure guards: Guards must be pure—no side effects, no external mutations.
❌ Subscribing to entire state: Use focused selectors with .
useSelector❌ Not memoizing model:
typescript
// WRONG
const model = Model.fromJson(layout); // New model every render
// CORRECT
const modelRef = useRef(Model.fromJson(layout));❌ 状态泛滥:为正交关注点使用扁平状态。应改用并行状态。
❌ 从动作中发送事件:切勿在内使用。使用处理内部事件。
assignsend()raise❌ 不纯守卫:守卫必须是纯函数——无副作用,无外部变更。
❌ 订阅整个状态:使用的聚焦选择器。
useSelector❌ 未记忆模型:
typescript
// 错误写法
const model = Model.fromJson(layout); // 每次渲染都会创建新模型
// 正确写法
const modelRef = useRef(Model.fromJson(layout));Navigation to References
参考文档导航
Core Patterns
核心模式
- xstate-v5-patterns.md: Complete v5 API, statecharts (hierarchy/parallel/history), promise actors
- react-integration.md: useMachine vs useActorRef, Context patterns, side effect handling
- testing-patterns.md: Unit testing, mocking actors, visualization debugging
- xstate-v5-patterns.md:完整v5 API、状态图(层级/并行/历史)、Promise Actor
- react-integration.md:useMachine与useActorRef对比、Context模式、副作用处理
- testing-patterns.md:单元测试、Actor模拟、可视化调试
Decision Making & Best Practices
决策与最佳实践
- decision-trees.md: When to use state machines vs useState/useReducer/React Query, machine splitting strategies
- real-world-patterns.md: Complete examples - auth flows, file uploads, wizards, undo/redo, shopping carts
- error-handling.md: Error boundaries, retry strategies, circuit breakers, graceful degradation
- performance.md: Selector memoization, React.memo integration, machine splitting for performance
- decision-trees.md:状态机与useState/useReducer/React Query的选型对比、机器拆分策略
- real-world-patterns.md:完整实战示例 - 认证流程、文件上传、向导页、撤销/重做、购物车
- error-handling.md:错误边界、重试策略、断路器、优雅降级
- performance.md:选择器记忆化、React.memo集成、性能导向的机器拆分
Advanced Topics
进阶主题
- persistence-hydration.md: localStorage persistence, SSR/Next.js hydration, snapshot serialization
- migration-guide.md: Step-by-step migration from useState/useReducer with before/after examples
- composition-patterns.md: Actor communication, machine composition, higher-order machines, systemId
- skills-architecture.md: Input/output parameterization, library structure
- persistence-hydration.md:localStorage持久化、SSR/Next.js hydration、快照序列化
- migration-guide.md:从useState/useReducer迁移的分步指南,含前后对比示例
- composition-patterns.md:Actor通信、机器组合、高阶机器、systemId
- skills-architecture.md:输入/输出参数化、库结构
Key Reminders
关键提示
- setup() is the v5 way: Strong TypeScript inference, actor registration, action definitions
- Invoke for async, actions for sync: Actions are fire-and-forget; invoked actors have lifecycle
- Finite states for modes, context for data: Don't create states for every data variation
- Visualize first: Stately Studio (stately.ai/editor) makes machines living documentation
- setup()是v5的标准方式:强大的TypeScript类型推断、Actor注册、动作定义
- 异步逻辑用Invoke,同步逻辑用Actions:Actions是一次性执行;被调用的Actor具有生命周期
- 有限状态表示模式,上下文存储数据:不要为每个数据变体创建状态
- 先可视化:Stately Studio(stately.ai/editor)可将机器转化为可维护的文档
Red Flags
危险信号
- More than 3-4 boolean flags → Need state machine
- Writing to determine mode → States should be explicit
if (a && !b && c) - Bugs from invalid state combinations → Machine prevents impossible states
- Can't explain state transitions to stakeholders → Visualization solves this
- 超过3-4个布尔标志 → 需要状态机
- 通过编写来确定模式 → 状态应显式定义
if (a && !b && c) - 无效状态组合导致的Bug → 状态机可防止不可能的状态出现
- 无法向利益相关者解释状态转换 → 可视化可解决此问题
Related Skills
相关技能
- react: Parent skill for React patterns
- nextjs: Server/client state coordination
- test-driven-development: Test machines with createActor before UI integration
- react:React模式的父技能
- nextjs:服务端/客户端状态协调
- test-driven-development:在UI集成前使用createActor测试机器