Files
2025-11-29 18:01:45 +08:00

9.4 KiB

XState v5 Core API Reference

Machine Creation

createMachine(config)

Creates a state machine configuration.

import { createMachine } from 'xstate';

const machine = createMachine({
  id: 'machineId', // Unique identifier
  initial: 'stateName', // Initial state
  context: {}, // Initial context data
  types: {}, // TypeScript type definitions
  states: {}, // State definitions
  on: {}, // Global transitions
  entry: [], // Entry actions
  exit: [], // Exit actions
  after: {}, // Delayed transitions
  always: [], // Eventless transitions
  invoke: {}, // Invoked services
  tags: [], // State tags
  description: '', // Machine description
});

setup(config)

Configures reusable machine logic with strong typing.

import { setup } from 'xstate';

const machine = setup({
  types: {
    context: {} as ContextType,
    events: {} as EventType,
    input: {} as InputType,
    output: {} as OutputType,
  },
  actions: {
    // Named action implementations
    actionName: (context, event, meta) => {
      /* ... */
    },
  },
  guards: {
    // Named guard implementations
    guardName: (context, event, meta) => boolean,
  },
  actors: {
    // Actor logic definitions
    actorName: fromPromise(() => fetch('/api')),
  },
  delays: {
    // Named delay functions
    delayName: (context, event) => 1000,
  },
}).createMachine({
  // Machine configuration
});

Actor Creation and Management

createActor(logic, options?)

Creates an actor instance from machine or actor logic.

const actor = createActor(machine, {
  id: 'actorId', // Actor identifier
  input: {}, // Initial input
  snapshot: {}, // Restored snapshot
  systemId: 'systemId', // Actor system ID
  logger: console.log, // Custom logger
  inspect: (event) => {}, // Inspection handler
});

Actor Methods

// Lifecycle
actor.start(); // Start the actor
actor.stop(); // Stop the actor
actor.getSnapshot(); // Get current snapshot
actor.getPersistedSnapshot(); // Get persistable snapshot

// Communication
actor.send(event); // Send an event
actor.send({ type: 'EVENT' }); // Send event object

// Subscription
const subscription = actor.subscribe(observer);
subscription.unsubscribe();

// System
actor.system; // Access actor system

State Configuration

State Node Properties

{
  type: 'atomic' | 'compound' | 'parallel' | 'final' | 'history',
  initial: 'childStateName',        // For compound states
  context: {},                      // State-specific context
  states: {},                      // Child states
  on: {},                         // Transitions
  entry: [],                     // Entry actions
  exit: [],                     // Exit actions
  always: [],                  // Eventless transitions
  after: {},                  // Delayed transitions
  invoke: {},                // Invoked services
  tags: [],                 // State tags
  description: '',         // State description
  meta: {},              // Metadata
  history: 'shallow' | 'deep',  // For history states
}

Transition Configuration

{
  target: 'stateName' | ['state1', 'state2'],  // Target state(s)
  guard: 'guardName' | guardFunction,          // Condition
  actions: ['action1', 'action2'],            // Actions to execute
  reenter: boolean,                           // Re-enter state
  description: 'Transition description',     // Documentation
}

Actions

assign(assignment)

Updates machine context immutably.

// Function updater
assign({
  count: ({ context }) => context.count + 1,
  user: ({ context, event }) => event.user,
});

// Object updater
assign({ count: 5, user: null });

// Single property
assign(({ context }) => ({
  ...context,
  modified: true,
}));

raise(event)

Raises an event internally.

raise({ type: 'INTERNAL_EVENT' });
raise(({ context }) => ({
  type: 'DYNAMIC_EVENT',
  data: context.someValue,
}));

sendTo(actor, event)

Sends event to another actor.

sendTo('actorId', { type: 'MESSAGE' });
sendTo(({ context }) => context.someActor, { type: 'EVENT' });

emit(event)

Emits an event to parent actor.

emit({ type: 'CHILD_EVENT', data: value });

log(message)

Logs a message (for debugging).

log('State entered');
log(({ context }) => `Count: ${context.count}`);

stop(actorId)

Stops a spawned actor.

stop('childActorId');

cancel(sendId)

Cancels a delayed send.

cancel('delayedSendId');

enqueueActions(callback)

Enqueues actions conditionally at runtime. Replaces v4's pure() and choose().

import { enqueueActions } from 'xstate';

// Basic conditional actions
entry: enqueueActions(({ context, event, enqueue, check }) => {
  // Conditionally enqueue actions
  if (context.count > 0) {
    enqueue('logPositive');
  }

  // Use check() for guards
  if (check({ type: 'hasPermission' })) {
    enqueue('performSecureAction');
  }

  // Enqueue multiple actions
  enqueue([
    { type: 'log', params: { message: 'Processing' } },
    'processData',
    assign({ processing: true }),
  ]);
});

// With parameters
enqueueActions(({ context, enqueue }, params: { threshold: number }) => {
  if (context.value > params.threshold) {
    enqueue('handleHighValue');
  }
});

Guards

Guard Functions

// Inline guard
guard: ({ context, event }) => context.count > 0;

// Named guard
guard: 'isValid';

// Negated guard
guard: not('isInvalid');

// Combined guards
guard: and(['isValid', 'isAuthorized']);
guard: or(['isAdmin', 'isOwner']);

Guard Helpers

import { not, and, or, stateIn } from 'xstate';

not(guard); // Negates a guard
and([guard1, guard2]); // All must be true
or([guard1, guard2]); // At least one must be true
stateIn('state.path'); // Checks if in state

Invoked Services

Service Configuration

invoke: {
  id: 'serviceId',
  src: 'serviceName' | actorLogic,
  input: ({ context, event }) => ({}),
  onDone: {
    target: 'success',
    actions: assign({ data: ({ event }) => event.output }),
  },
  onError: {
    target: 'failure',
    actions: assign({ error: ({ event }) => event.error }),
  },
  onSnapshot: {
    actions: ({ event }) => console.log(event.snapshot),
  },
}

Multiple Invocations

invoke: [
  { id: 'service1', src: 'api1' },
  { id: 'service2', src: 'api2' },
];

Spawning Actors

spawn(logic, options?)

Spawns a child actor.

import { spawn } from 'xstate';

// In an action
spawn(childMachine, {
  id: 'childId',
  systemId: 'childSystem',
  input: { initial: 'data' },
  syncSnapshot: true,
});

stopChild(actorId)

Stops a spawned child actor.

stopChild('childId');

Delayed Transitions

After Configuration

after: {
  1000: 'timeout',                    // Fixed delay
  DELAY_NAME: 'delayed',             // Named delay
  [({ context }) => context.delay]: 'dynamic',  // Dynamic delay
}

Eventless Transitions

Always Configuration

always: [
  { target: 'state1', guard: 'condition1' },
  { target: 'state2', guard: 'condition2' },
  { target: 'default' }, // Fallback
];

Utility Functions

waitFor(actor, predicate, options?)

Waits for an actor to reach a specific condition.

const snapshot = await waitFor(actor, (snapshot) => snapshot.matches('done'), {
  timeout: 5000,
});

toPromise(actor)

Converts an actor to a Promise.

const result = await toPromise(actor);

createEmptyActor()

Creates an actor that does nothing.

const emptyActor = createEmptyActor();

Type Helpers

ActorRefFrom<T>

Gets the ActorRef type from logic.

type MyActorRef = ActorRefFrom<typeof machine>;

SnapshotFrom<T>

Gets the Snapshot type from logic or ActorRef.

type MySnapshot = SnapshotFrom<typeof machine>;

EventFromLogic<T>

Gets the event union type from logic.

type MyEvents = EventFromLogic<typeof machine>;

StateValueFrom<T>

Gets the state value type from logic.

type MyStateValue = StateValueFrom<typeof machine>;

ContextFrom<T>

Gets the context type from logic.

type MyContext = ContextFrom<typeof machine>;

Inspection and Debugging

Inspector API

const actor = createActor(machine, {
  inspect: (inspectionEvent) => {
    if (inspectionEvent.type === '@xstate.snapshot') {
      console.log('Snapshot:', inspectionEvent.snapshot);
    }
  },
});

Inspection Event Types

  • @xstate.actor - Actor created/stopped
  • @xstate.snapshot - Snapshot updated
  • @xstate.event - Event processed
  • @xstate.microstep - Microstep taken

State Methods

state.matches(stateValue)

Checks if in a specific state.

if (state.matches('loading')) {
  /* ... */
}
if (state.matches({ form: 'valid' })) {
  /* ... */
}

state.hasTag(tag)

Checks if state has a tag.

if (state.hasTag('loading')) {
  /* ... */
}

state.can(event)

Checks if an event can cause a transition.

if (state.can({ type: 'SUBMIT' })) {
  /* ... */
}

state.nextEvents

Gets available events from current state.

const events = state.nextEvents; // ['SUBMIT', 'CANCEL']