Skip to content

Actions

Actions 是“触发后就执行、不关心返回结果的副作用”

状态机发生 transition 时,可能会执行 actions。

什么时候执行actions

事件触发 → 状态切换 → 执行 actions

transition

ts
on: {
  EVENT: {
    target: 'next',
    actions: [...]
  }
}

entry

只要进入该状态,就会执行

ts
entry: [...]

exit

只要离开该状态,就会执行

ts
exit: [...]

actions执行顺序

ts
event  →  transition
          ├─ exit actions(原状态)
          ├─ transition actions(事件触发)
          ├─ entry actions(新状态)

        new state

Actions 是“事件发生后的副作用钩子”,可以挂在 transition、entry、exit 上,但不会影响状态流转

Actions特点

Actions 可以是单个,也可以是数组

ts
actions: { type: 'track' }
ts
actions: [
  { type: 'track' },
  { type: 'showConfetti' }
]

Action之间没有依赖性

  • actions 是按数组顺序被“遍历执行”的,

  • 但所有 assigncontext 的修改是“同时生效”的

  • action 之间不会依赖彼此已经修改过的 context

Action 不能是异步函数

XState 在处理 transition 时不会等待 action 完成

异步函数会返回 Promise,但状态机不会等待它,也不会把它纳入状态流控制。

ts
actions: [
  async ({ context }) => {
    await fetch('/api')
  }
]

创建actions

setup

ts
const feedbackMachine = setup({
  actions: {
    track: (_, params: { response: string }) => {
      trackResponse(params.response);
    },
    showConfetti: () => {
      // ...
    }
  }
})
  • _{ context, event, self }

  • params :any, action 调用时传入的参数

Inline actions

可以直接在状态机中声明actions。适合简单的状态机

ts
callback({context,event})
ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  entry: [
    // Inline action
    ({ context, event }) => {
      console.log(/* ... */);
    },
  ],
});

复用已有Action

使用自定义Action,有两种方式:

string

如果你不需要传参数,可以使用字符串简写

ts
actions: ['track']
ts
const feedbackMachine = createMachine({
  states: {
    question: {
      on: {
        'feedback.good': {
          actions: ['track'], // ✅ 简写方式
        },
      },
    },
  },
});

Action Object

ts
interface Action{
  type:string
  params:any
}
  • type:在implementation中定义的action函数名
  • params:传入函数中的参数

如果你需要在复用actions传参数,可以使用对象简写

ts
actions: [{ type: 'track', params: {...} }]
ts
actions: [
  { type: 'track', params: { response: 'good' } }
]

内置 actions

警告

不能在自定义actions中调用内置actions

assign

更新 context

ts
assign({
  count: ({ context }) => context.count + 1
})

多个action

如果存在多个action,其中一个action使用了assign改变了context,下一个action中的context并没有改变。下一个内部事件里将看到更新后的context

ts
actions: [
  // context.count=1
  assign({
    count: ({ context }) => context.count + 1,
  }),

  // context.count=1
  ({ context }) => {
    if (context.count + 1 >= 3) {
      return raise({ type: 'DONE' });
    }
  },
],
  • 写法一:对象形式【推荐】
ts
assign({
  count: ({ context, event }) => context.count + event.value,
})
  • 写法二:函数形式
ts
assign(({ context, event }) => {
  return {
    count: context.count + event.value,
  }
})

sendTo

触发其他actor的事件

ts
const machine = createMachine({
  on: {
    note: {
      actions: sendTo('someActor', { type: 'someEvent' }),
    },
  },
});

raise

raise 就是“在状态机内部触发一个事件”,并且会被立刻处理

ts
raise(EventObject)
对比raisesend / service.send
事件来源状态机内部外部
目标当前状态机自己当前 / 其他 actor
处理时机当前 transition 结束后立即下一轮事件循环
是否能跨 actor
常用场景内部流程衔接UI / 网络 / 用户行为
ts
INC: {
  actions: [
    assign({ count: ({ context }) => context.count + 1 }),
    raise({ type: 'CHECK' }),//一次 INC,立刻进入 done
  ],
},
CHECK: {
  guard: ({ context }) => context.count >= 3,
  target: 'done',
},

log

log action可以方便的打印日志到控制台

ts
import { createMachine, log } from 'xstate';

const machine = createMachine({
  on: {
    someEvent: {
      actions: log('some message'),
    },
  },
});

cancel

取消之前由 sendToraise 创建的延迟事件或定时任务。通过 id 来对应要取消的动作。可以类比于定时器

ts
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。函数执行是同步的,上一个函数执行完成下一个函数才会继续执行。

ts
actions: [
  assign({ count: c => c + 1 }),
  'logCount',
]
ts
enqueueActions(({ enqueue }) => {
  enqueue.assign(...)
  enqueue('logCount')
})

enqueueActions唯一允许你“命令式使用内置 actions”的地方

ts
enqueue.assign(...)
enqueue.raise({ type: 'NEXT' })
enqueue.sendTo(...)

examples

ts
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('🎉 已达到最大值');
        },
      ],
    },
  },
});