Options
Statecharts require that a machine is separated into two parts. The first part is the machine’s config. The config describes how the machine behaves — its states, events and transitions.
The second part is a machine’s options — implementation details that expand the machine’s capabilities.
Options can:
- Make decisions based on checks, such as an
if/else
statement - Make changes to the machine’s surrounding environment
- Subscribe to changes from the machine’s outside environment
In XState, the separation is divided between:
Term | Description |
---|---|
Config | What the machine does |
Options | How the machine does it |
Below is an example where we describe in our config that when the machine first starts, it 'says hello', using the sayHello
action.
const helloMachine = createMachine({
/**
* Below is an 'action' — we’ll
* learn more about actions later
*/
entry: ['sayHello'],
});
As a visualization, the config is readable. But the machine doesn’t do anything yet.
Options let us pass an implementation for the sayHello
action.
const helloMachine = createMachine(
{
entry: ['sayHello'],
},
{
actions: {
sayHello: () => {
console.log('Hello!');
},
},
}
);
The separation between “what your code does” and “how your code does it” is powerful because it allows you to understand its purpose without requiring you to read through the implementation details.
Option types
There are four types of options you can pass to your statechart.
Guards
Guards allow you to check something before you proceed, enabling you to implement if/else logic in XState.
Below is an example of a guard:
import { createMachine } from 'xstate';
let iAmHappyAndIKnowIt = true;
const machine = createMachine(
{
initial: 'notClappingHands',
states: {
notClappingHands: {
on: {
HEAR_MUSIC: {
/**
* Name the guard…
*/
cond: 'ifYoureHappyAndYouKnowIt',
target: 'clappingHands',
},
},
},
clappingHands: {},
},
},
{
guards: {
/**
* …then implement the guard.
*/
ifYoureHappyAndYouKnowIt: () => {
return iAmHappyAndIKnowIt;
},
},
}
);
In the example above, the guard is activated when the machine is in the notClappingHands
state and reaches the HEAR_MUSIC
event. If the ifYoureHappyAndYouKnowIt
guard is true, the machine will go to the clappingHands
state.
Actions
Actions allow you to perform simple actions, such as assigning to a variable or calling synchronous actions:
import { createMachine } from 'xstate';
const machine = createMachine(
{
entry: ['sayHello'],
},
{
actions: {
sayHello: () => {
console.log('Hello!');
},
},
}
);
In the example above, sayHello
will be called when the machine is started.
Actors
Actors are entities that have their own state and can send and receive events. Actors are useful for processes like:
- Subscribing to a websocket/DOM listener for updates
- Invoking a promise and waiting for it to resolve
- Uploading a file
We’ll revisit actors in more depth later. Take a fast track to learn more about actors.
Delays
Delays are used in XState to represent timers and intervals. We’ll revisit delays in more depth later. Take a fast track to learn more about delays.
Options API
You can specify machine options in several places. Firstly, you can set options inside the machine itself:
import { createMachine } from 'xstate';
const machine = createMachine(
{
// Set config here
},
{
// Set options here:
actions: {},
// `actors` in v5
services: {},
guards: {},
delays: {},
}
);
Or, you can specify options later with a .withConfig
call:
import { createMachine } from 'xstate';
const machine = createMachine({});
// ---cut---
machine.withConfig({
actions: {},
// `actors` in v5
services: {},
guards: {},
delays: {},
});
Summary
Options are how you make your machine do things. They allow machines to keep control of the config and enable you to pass in options later.