Skip to content
Version: XState v4

Actions vs. actors

Sometimes it’s unclear whether you should use an action or an actor. Both appear to do similar things, executing side effects. Let’s break down the differences:

Actions are “fire-and-forget”; as soon as their execution starts, the statechart running the actions forgets about them. If you specify an action as async, the action won’t be awaited before moving to the next state. Below is an example:

const machine = createMachine(
{
context: {
userName: '',
},
initial: 'collectingFormDetails',
states: {
collectingFormDetails: {
on: {
SUBMIT: {
actions: 'submitForm',
target: 'submitted',
},
},
},
submitted: {},
},
},
{
actions: {
submitForm: async (context) => {
await createUser(context.userName);
},
},
},
);

You might think that the sequence would work as follows:

  1. In the collectingFormDetails state, we receive the SUBMIT event.
  2. We execute the submitForm action and wait for it to finish.
  3. When the submitForm action is done, we go to the submitted state.

Instead, the sequence works like this:

  1. In the collectingFormDetails state, we receive the SUBMIT event.
  2. We execute the submitForm action and immediately transition to the submitted state.
  3. The result of the submitForm action is ignored.

To handle submitForm properly, we need to use an actor:

const machine = createMachine(
{
context: {
userName: '',
},
initial: 'collectingFormDetails',
states: {
collectingFormDetails: {
on: {
SUBMIT: {
target: 'submitting',
},
},
},
submitting: {
invoke: {
src: 'submitForm',
onDone: {
target: 'submitted',
},
onError: {
target: 'errored',
},
},
},
errored: {},
submitted: {},
},
},
{
// `actors` in v5
services: {
submitForm: async (context) => {
await createUser(context.userName);
},
},
},
);

Now, the sequence in the example above is:

  1. In the collectingFormDetails state, we receive the SUBMIT event.
  2. We go to the submitting state, where we execute the submitForm actor.
  3. When the submitForm actor is done, we go to the submitted state.
  4. If the submitForm actor errors, we go to the errored state.

The main difference between actions and actors is that actions can’t communicate back to the machine. Actors can.