DEV Community

Cover image for When actions fire in xstate

When actions fire in xstate

Michal Bryxí
Cycle 🚴 , climb 🗻 , run 🏃 , travel 🌍 , enjoy life ♥. IT guy with the need to live fully.
Updated on ・2 min read

While working with statecharts in the popular xstate library I fell into a trap of not understanding how and when are actions fired.

Most of the time it does not really matter. Until it does. That until happened when I tried to "smartly" use transient transitions.

Given following statechart:

const machine = Machine(
    initial: 'idle',
    context: {
      winning: 'heads',
      selected: 'tails'
    states: {
      idle: {
        on: {
          SELECT: [
            { target: 'playing' }
      playing: {
        // It might seem that entry level action fires first.
        entry: ['flipCoin'],
        on: {
          '': [
            // And *after* that the guard is checked.
            // But this is not how it works.
            { target: 'score', cond: 'isScore' },
            { target: 'nope' }
      nope: {
        type: 'final'
      score: {
        type: 'final'
  }, {
    guards: {
      isScore(context) {
        console.log('guard isScore');
        return context.selected === context.winning;
    actions: {
      flipCoin(context) {
        context.selected = 'heads';
        console.log('action flipCoin');
Enter fullscreen mode Exit fullscreen mode

one might assume that given the entry action called flipCoin, which will change state selected='heads' the guard isScore will return true and thus we will end in the score final state.

But this is not how things work. According to the documentation:

Actions are not immediately triggered. Instead, the State object returned from machine.transition(...) will declaratively provide an array of .actions that an interpreter can then execute.

So in my head:

After event is sent to xstate, the next state of the statechart is determined based on current context before any actions are fired.

Photo by Clyde He on Unsplash

Discussion (0)