Using XState, how can I access name of current state in an action?

4.2k views Asked by At

I'm playing around learning XState and wanted to include an action in a machine that would just log the current state to console.

Defining a simple example machine like so, how would I go about this? Also note the questions in the comments in the code.

import { createMachine, interpret } from "xstate"

const sm = createMachine({
    initial: 'foo',
    states: {
        foo: {
            entry: 'logState', // Can I only reference an action by string?
                               // Or can I add arguments here somehow?
            on: {
                TOGGLE: {target: 'bar'}
            }
        },
        bar: {
            entry: 'logState',
            on: {
                TOGGLE: {target: 'foo'}
            }
        }
    }
},
{
    actions: {
        logState(/* What arguments can go here? */) => {
            // What do I do here?
        }
    }
});

I know that actions are called with context and event as arguments but I don't see a way to get the current state from either of those. Am I missing something here?

2

There are 2 answers

3
Mr. Hedgehog On

Actions receive three arguments - context, event and meta. meta have property state, which is current state.

import { createMachine } from "xstate";

let metaDemo = createMachine(
  {
    id: "meta-demo",
    initial: "ping",
    states: {
      ping: {
        entry: ["logStateValues"],
        after: { TIMEOUT: "pong" },
      },
      pong: {
        entry: ["logStateValues"],
        after: { TIMEOUT: "ping" },
      },
    },
  },
  {
    delays: {
      TIMEOUT: 3000,
    },
    actions: {
      logStateValues(ctx, event, meta) {
        if (meta.state.matches("ping")) {
          console.log("It's PING!");
        } else if (meta.state.matches("pong")) {
          console.log("And now it's PONG");
        } else {
          console.log(
            `This is not supposed to happen. State is: ${meta.state
              .toStrings()
              .join(".")}`
          );
        }
      },
    },
  }
);
0
guowy On

For a simple use case like yours, you could try recording the state on transition.

let currentState;
const service = interpret(machine).onTransition(state => {
   if (state.value != currentState) {
       // TODO: terminate timer if any and start a new one
      currentState = state.value;
   }
});

Then use the value in your actions.

See more here: https://github.com/statelyai/xstate/discussions/1294