前端工程Technical Deep Dive
状态架构模式 —— 状态机与可预测状态管理
发布时间2026/03/29
分类前端工程
预计阅读2 分钟
作者吴长龙
*
状态管理的困境
01.内容
状态管理的困境
javascript snippetjavascript
// 传统状态管理的问题
const [state, setState] = useState({
loading: false,
data: null,
error: null,
// 状态组合越来越多...
});
// 问题:
// 1. 状态互斥关系不明确(loading 和 data 不能同时存在?)
// 2. 状态流转不清晰(从哪来?到哪去?)
// 3. 状态组合爆炸(n 个布尔值 = 2^n 种可能)
// 4. 难以追踪变化(谁改的?为什么改?)状态机基础
#### 什么是状态机?
状态机是一种数学模型,描述系统在不同状态之间的转换:
code snippetcode
┌─────────┐
│ idle │ ←─── 初始状态
└────┬────┘
│ 事件: START
↓
┌─────────┐
│ loading │ ←─── 进行中
└────┬────┘
│ 事件: SUCCESS
↓
┌─────────┐
│ success │ ←─── 成功
└────┬────┘
│ 事件: ERROR
↓
┌─────────┐
│ error │ ←─── 失败
└─────────┘#### 状态机核心概念
- •State(状态):系统在某时刻的条件
- •Event(事件):触发状态转换的动作
- •Transition(转换):状态之间的变化关系
- •Action(动作):状态变化时执行的操作
- •Guard(守卫):决定是否允许转换的条件
XState:状态机库
#### 基本用法
javascript snippetjavascript
import { createMachine, interpret } from 'xstate';
// 定义状态机
const fetchMachine = createMachine({
id: 'fetch',
initial: 'idle',
states: {
idle: {
on: { FETCH: 'loading' }
},
loading: {
after: {
10000: 'failure.timeout' // 超时转换
},
on: {
RESOLVE: 'success',
REJECT: 'failure'
}
},
success: {
type: 'final' // 终态
},
failure: {
on: { RETRY: 'loading' }
}
}
});
// 创建服务
const fetchService = interpret(fetchMachine)
.onTransition(state => console.log(state.value))
.start();
fetchService.send({ type: 'FETCH' });
fetchService.send({ type: 'RESOLVE', data: {} });#### 完整示例:用户登录
javascript snippetjavascript
const loginMachine = createMachine({
id: 'login',
initial: 'idle',
context: {
username: '',
password: '',
error: null,
user: null,
},
states: {
idle: {
on: {
UPDATE_USERNAME: {
actions: assign({ username: (_, event) => event.value })
},
UPDATE_PASSWORD: {
actions: assign({ password: (_, event) => event.value })
},
SUBMIT: 'submitting'
}
},
submitting: {
invoke: {
src: 'loginApi',
onDone: {
target: 'success',
actions: assign({ user: (_, event) => event.data })
},
onError: {
target: 'failure',
actions: assign({ error: (_, event) => event.data })
}
}
},
success: {
on: {
LOGOUT: {
target: 'idle',
actions: assign({ user: null, username: '', password: '' })
}
}
},
failure: {
on: {
RETRY: 'idle',
SUBMIT: 'submitting'
}
}
}
});在 React 中使用 XState
javascript snippetjavascript
import { useMachine } from '@xstate/react';
function LoginForm() {
const [state, send] = useMachine(loginMachine);
const { username, password, error } = state.context;
if (state.matches('success')) {
return <div>欢迎,{state.context.user.name}!</div>;
}
return (
<form onSubmit={() => send({ type: 'SUBMIT' })}>
<input
value={username}
onChange={(e) => send({ type: 'UPDATE_USERNAME', value: e.target.value })}
/>
<input
type="password"
value={password}
onChange={(e) => send({ type: 'UPDATE_PASSWORD', value: e.target.value })}
/>
{state.matches('failure') && <div className="error">{error}</div>}
<button
type="submit"
disabled={state.matches('submitting')}
>
{state.matches('submitting') ? '登录中...' : '登录'}
</button>
</form>
);
}视觉化状态机
javascript snippetjavascript
// XState 支持生成可视化图表
const machine = createMachine({
// ... 配置
});
// 使用 @xstate/inspector 可视化
import { inspect } from '@xstate/inspect';
inspect({
url: 'https://statecharts.io/inspect',
iframe: false,
});状态机 vs 传统状态管理
| 特性 | 传统 useState | 状态机 |
|---|---|---|
| 状态关系 | 隐式 | 显式 |
| 状态转换 | 随意 | 约束 |
| 状态数量 | 指数增长 | 线性 |
| 可预测性 | 低 | 高 |
| 调试 | 困难 | 容易 |
实际应用场景
#### 1. 订单流程
javascript snippetjavascript
const orderMachine = createMachine({
id: 'order',
initial: 'cart',
states: {
cart: {
on: { CHECKOUT: 'address' }
},
address: {
on: {
NEXT: 'payment',
BACK: 'cart'
}
},
payment: {
on: {
SUBMIT: 'processing',
BACK: 'address'
}
},
processing: {
invoke: {
src: 'processOrder',
onDone: 'confirmation',
onError: 'payment'
}
},
confirmation: {
type: 'final'
}
}
});#### 2. 播放器
javascript snippetjavascript
const playerMachine = createMachine({
id: 'player',
initial: 'ready',
states: {
ready: {
on: { PLAY: 'playing' }
},
playing: {
on: {
PAUSE: 'paused',
END: 'ended'
},
after: {
30000: 'idle' // 30秒无操作
}
},
paused: {
on: {
PLAY: 'playing',
STOP: 'idle'
}
},
ended: {
on: {
REPLAY: 'playing',
STOP: 'idle'
}
},
idle: {
on: { PLAY: 'ready' }
}
}
});#### 3. 表单验证
javascript snippetjavascript
const formMachine = createMachine({
id: 'form',
initial: 'editing',
context: {
values: {},
errors: {}
},
states: {
editing: {
on: {
CHANGE: {
actions: assign({
values: (context, event) => ({
...context.values,
[event.field]: event.value
})
})
},
VALIDATE: [
{ target: 'validating', cond: 'isValid' }
]
}
},
validating: {
invoke: {
src: 'validate',
onDone: {
target: 'valid',
actions: assign({ errors: (_, event) => event.data })
},
onError: {
target: 'error'
}
}
},
valid: {
on: { SUBMIT: 'submitting' }
},
error: {
on: { CHANGE: 'editing' }
}
}
});状态机 + React Query
javascript snippetjavascript
const fetchMachine = createMachine({
id: 'fetch',
initial: 'idle',
context: {
data: null,
error: null
},
states: {
idle: {
on: { FETCH: 'loading' }
},
loading: {
invoke: {
src: 'fetchData',
onDone: {
target: 'success',
actions: assign({ data: (_, event) => event.data })
},
onError: {
target: 'error',
actions: assign({ error: (_, event) => event.data })
}
}
},
success: {
on: { FETCH: 'loading' }
},
error: {
on: { FETCH: 'loading' }
}
}
});
// 结合 React Query
function useFetchMachine() {
const [state, send] = useMachine(fetchMachine, {
services: {
fetchData: async () => {
return await queryClient.fetchQuery({
queryKey: ['data'],
queryFn: fetchData
});
}
}
});
return { state, refetch: () => send({ type: 'FETCH' }) };
}总结
状态机让状态管理可预测、可追溯、可测试:
- •状态关系显式:避免非法状态组合
- •转换有据可查:每个状态变化都有明确原因
- •易于测试:每个状态和转换都可独立测试
- •文档友好:状态图本身就是文档
推荐工具:XState(最成熟)或 Zustand + XState(轻量组合)。