Parallel states
A parallel state is a state separated into multiple regions of child states, where each region is active simultaneously.
You can specify a parallel state node on the machine and/or any nested child state by setting type: 'parallel'
.
For example, the machine below allows the upload
and download
child states to be simultaneously active. This example represents an application where you can download and upload files at the same time:
import { createMachine } from 'xstate';
const fileMachine = createMachine({
type: 'parallel',
states: {
upload: {
initial: 'idle',
states: {
idle: {
on: {
INIT_UPLOAD: { target: 'pending' },
},
},
pending: {
on: {
UPLOAD_COMPLETE: { target: 'success' },
},
},
success: {},
},
},
download: {
initial: 'idle',
states: {
idle: {
on: {
INIT_DOWNLOAD: { target: 'pending' },
},
},
pending: {
on: {
DOWNLOAD_COMPLETE: { target: 'success' },
},
},
success: {},
},
},
},
});
Parallel states allow you to run processes simultaneously for greater flexibility and power.
Matching parallel states​
The state.matches
syntax checks if an actor is in a particular state when the machine is run using interpret
or similar. You can use state.matches
with parent states using either the string method or the object method.
state.matches
string argument​
import { createMachine } from 'xstate';
const machine = createMachine({});
const state = machine.initialState;
/**
* Checks if the machine is in the red.stop
* state
*/
console.log(state.matches('red.stop'));
state.matches
object argument​
import { createMachine } from 'xstate';
const machine = createMachine({});
const state = machine.initialState;
/**
* Checks if the machine is in the red.stop
* state
*/
state.matches({
red: 'stop',
});
You can also use the object method to check if your machine is in two parallel states at once:
import { createMachine } from 'xstate';
const machine = createMachine({});
const state = machine.initialState;
/**
* Checks if the machine is in the wind.blowing
* state AND the rain.raining state
*/
state.matches({
wind: 'blowing',
rain: 'raining',
});
Match one of multiple states​
If you want to match one of multiple states, you can use .some()
on an array of state values:
import { createMachine } from 'xstate';
const machine = createMachine({});
const state = machine.initialState;
const isMatch = [{ customer: 'deposit' }, { customer: 'withdrawal' }].some(
state.matches,
);
Targeting multiple parallel states​
Sometimes you’ll need a transition to target multiple parts of a parallel state simultaneously. This is useful when you want to affect different parts of a simultaneous process at the same time.
Multiple targets are specified as an array in target: [...]
.
import { createMachine } from 'xstate';
const settingsMachine = createMachine({
type: 'parallel',
states: {
mode: {
initial: 'active',
states: {
inactive: {},
pending: {},
active: {},
},
},
status: {
initial: 'enabled',
states: {
disabled: {},
enabled: {},
},
},
},
on: {
// Multiple targets
DEACTIVATE: {
target: ['.mode.inactive', '.status.disabled'],
},
},
});
In the example above, the DEACTIVATE
event simultaneously causes two changes, disabling the status
and turning the mode
to inactive
.
Final states and parallel states​
When every child state node in a parallel state node is done, the parent parallel state node is also done. When every final state node in every child state node is reached, the done
event will be fired for the parallel state node.
onDone
is very useful in modeling parallel tasks. For example, below there is a shopping machine where user
and items
represent two parallel tasks of the cart
state:
import { createMachine } from 'xstate';
const shoppingMachine = createMachine({
initial: 'cart',
states: {
cart: {
type: 'parallel',
states: {
user: {
initial: 'pending',
states: {
pending: {
entry: 'getUser',
on: {
RESOLVE_USER: { target: 'success' },
REJECT_USER: { target: 'failure' },
},
},
success: { type: 'final' },
failure: {},
},
},
items: {
initial: 'pending',
states: {
pending: {
entry: 'getItems',
on: {
RESOLVE_ITEMS: { target: 'success' },
REJECT_ITEMS: { target: 'failure' },
},
},
success: { type: 'final' },
failure: {},
},
},
},
onDone: 'confirm',
},
confirm: {},
},
});
The onDone
transition will only happen when all of the child states of cart
(e.g., user
and items
) are in their final states. In the case of the shopping machine, once the shopping.cart.user.success
and shopping.cart.items.success
state nodes are reached, the machine will transition from the cart
to the confirm
state.