Using context in component

Again, using the machine inside a component can be done using useActor.

This time we access the current context using snapshot.context. From context we extract the toggleValue, and we use it for the checkbox checked property:

import { useActor } from "@xstate/react";
import { assign, setup } from "xstate";

type Event = { type: "toggle" };
type Context = { toggleValue: boolean };
const initialContext: Context = { toggleValue: false };

const machine = setup({
  types: {
    events: {} as Event,
    context: {} as Context,
  },
}).createMachine({
  context: initialContext,
  initial: "Idle",
  states: {
    Idle: {
      on: {
        toggle: {
          target: "Idle",
          actions: assign(({ context }) => ({
            toggleValue: !context.toggleValue,
          })),
        },
      },
    },
  },
});

export default function MachineContext() {
  const [snapshot, send] = useActor(machine);
  return (
    <input
      type="checkbox"
      checked={snapshot.context.toggleValue}
      onChange={() => send({ type: "toggle" })}
    />
  );
}

We learned how to define a state machine with context and how to update the context using actions:

  1. Define the context type inside setup/context
  2. Define the initial context value inside createMachine
  3. Capture the "toggle" event inside the "Idle" state
  4. Define and execute actions

We can now compare this context-based machine with the previous state-based machine:

type Event = { type: "toggle" };
type Context = { toggleValue: boolean };
const initialContext: Context = { toggleValue: false };

const machine = setup({
  types: {
    events: {} as Event,
    context: {} as Context,
  },
}).createMachine({
  context: initialContext,
  initial: "Idle",
  states: {
    Idle: {
      on: {
        toggle: {
          target: "Idle",
          actions: assign(({ context }) => ({
            toggleValue: !context.toggleValue,
          })),
        },
      },
    },
  },
});
type Event = { type: "toggle" };

const machine = setup({
  types: {
    events: {} as Event,
  },
}).createMachine({
  initial: "Off",
  states: {
    Off: {
      on: {
        toggle: { target: "On" },
      },
    },
    On: {
      on: {
        toggle: { target: "Off" },
      },
    },
  },
});

XState machines combine both context and states to allow to model any kind of state. Again, we are going to appreciate the advantages of this state-based approach as we explore more complex use cases in the course.

For now this allowed us to compare useState, useReducer, and fromTransition with state-based and context-based state machines.

Github repository