Actions
Actions 是“触发后就执行、不关心返回结果的副作用”
当状态机发生 transition 时,可能会执行 actions。
什么时候执行actions
事件触发 → 状态切换 → 执行 actions
transition
on: {
EVENT: {
target: 'next',
actions: [...]
}
}entry
只要进入该状态,就会执行
entry: [...]exit
只要离开该状态,就会执行
exit: [...]actions执行顺序
event → transition
├─ exit actions(原状态)
├─ transition actions(事件触发)
├─ entry actions(新状态)
↓
new stateActions 是“事件发生后的副作用钩子”,可以挂在 transition、entry、exit 上,但不会影响状态流转
Actions特点
Actions 可以是单个,也可以是数组
actions: { type: 'track' }actions: [
{ type: 'track' },
{ type: 'showConfetti' }
]Action之间没有依赖性
actions 是按数组顺序被“遍历执行”的,
但所有
assign对context的修改是“同时生效”的action 之间不会依赖彼此已经修改过的 context
Action 不能是异步函数
XState 在处理 transition 时不会等待 action 完成
异步函数会返回 Promise,但状态机不会等待它,也不会把它纳入状态流控制。
actions: [
async ({ context }) => {
await fetch('/api')
}
]创建actions
setup
const feedbackMachine = setup({
actions: {
track: (_, params: { response: string }) => {
trackResponse(params.response);
},
showConfetti: () => {
// ...
}
}
})_:{ context, event, self }params:any, action 调用时传入的参数
Inline actions
可以直接在状态机中声明actions。适合简单的状态机
callback({context,event})import { createMachine } from 'xstate';
const feedbackMachine = createMachine({
entry: [
// Inline action
({ context, event }) => {
console.log(/* ... */);
},
],
});复用已有Action
使用自定义Action,有两种方式:
string
如果你不需要传参数,可以使用字符串简写
actions: ['track']const feedbackMachine = createMachine({
states: {
question: {
on: {
'feedback.good': {
actions: ['track'], // ✅ 简写方式
},
},
},
},
});Action Object
interface Action{
type:string
params:any
}- type:在implementation中定义的action函数名
- params:传入函数中的参数
如果你需要在复用actions传参数,可以使用对象简写
actions: [{ type: 'track', params: {...} }]actions: [
{ type: 'track', params: { response: 'good' } }
]内置 actions
警告
不能在自定义actions中调用内置actions
assign
更新 context
assign({
count: ({ context }) => context.count + 1
})多个action
如果存在多个action,其中一个action使用了assign改变了context,下一个action中的context并没有改变。下一个内部事件里将看到更新后的context
actions: [
// context.count=1
assign({
count: ({ context }) => context.count + 1,
}),
// context.count=1
({ context }) => {
if (context.count + 1 >= 3) {
return raise({ type: 'DONE' });
}
},
],- 写法一:对象形式【推荐】
assign({
count: ({ context, event }) => context.count + event.value,
})- 写法二:函数形式
assign(({ context, event }) => {
return {
count: context.count + event.value,
}
})sendTo
触发其他actor的事件
const machine = createMachine({
on: {
note: {
actions: sendTo('someActor', { type: 'someEvent' }),
},
},
});raise
raise 就是“在状态机内部触发一个事件”,并且会被立刻处理
raise(EventObject)| 对比 | raise | send / service.send |
|---|---|---|
| 事件来源 | 状态机内部 | 外部 |
| 目标 | 当前状态机自己 | 当前 / 其他 actor |
| 处理时机 | 当前 transition 结束后立即 | 下一轮事件循环 |
| 是否能跨 actor | ❌ | ✅ |
| 常用场景 | 内部流程衔接 | UI / 网络 / 用户行为 |
INC: {
actions: [
assign({ count: ({ context }) => context.count + 1 }),
raise({ type: 'CHECK' }),//一次 INC,立刻进入 done
],
},
CHECK: {
guard: ({ context }) => context.count >= 3,
target: 'done',
},log
log action可以方便的打印日志到控制台
import { createMachine, log } from 'xstate';
const machine = createMachine({
on: {
someEvent: {
actions: log('some message'),
},
},
});cancel
取消之前由 sendTo 或 raise 创建的延迟事件或定时任务。通过 id 来对应要取消的动作。可以类比于定时器
import { createMachine, sendTo, cancel } from 'xstate';
const machine = createMachine({
on: {
event: {
actions: sendTo(
'someActor', // 目标 actor
{ type: 'someEvent' }, // 要发送的事件
{
id: 'someId', // 用于 cancel 的唯一标识
delay: 1000, // 延迟 1 秒
}
),
},
cancelEvent: {
actions: cancel('someId'), // 取消上面 id 为 someId 的延迟事件
},
},
});enqueueActions
actions的执行顺序是由xstate决定的,如果对actions的执行顺序有强烈的需要可以使用enqueueActions。类似与async-await。函数执行是同步的,上一个函数执行完成下一个函数才会继续执行。
actions: [
assign({ count: c => c + 1 }),
'logCount',
]enqueueActions(({ enqueue }) => {
enqueue.assign(...)
enqueue('logCount')
})enqueueActions 是 唯一允许你“命令式使用内置 actions”的地方:
enqueue.assign(...)
enqueue.raise({ type: 'NEXT' })
enqueue.sendTo(...)examples
import { createMachine, assign, raise } from 'xstate';
const counterMachine = createMachine({
id: 'counter',
initial: 'active',
context: {
count: 0,
},
states: {
active: {
on: {
INC: {
actions: [
// ✅ 内置 action:更新 context
assign({
count: ({ context }) => context.count + 1,
}),
// ✅ 自定义 action:副作用(日志)
({ context }) => {
console.log('当前 count:', context.count + 1);
},
// ✅ 内置 action:根据条件触发内部事件
({ context }) => {
if (context.count + 1 >= 3) {
// ❌ 不能直接 raise()
// raise({ type: 'REACHED_MAX' })
// ✅ 正确:返回 built-in action
return raise({ type: 'REACHED_MAX' });
}
},
],
},
REACHED_MAX: {
target: 'done',
},
},
},
done: {
entry: [
// ✅ 自定义 action
() => {
console.log('🎉 已达到最大值');
},
],
},
},
});