Skip to content
Version: XState v4

Context

Statecharts represent their finite state using states but can also handle states which are not finite. These states might be:

  • Counters, which can be incremented as many times as required.
  • Text inputs, where the user might enter any value.

This “infinite” state can be stored in a statechart’s context, a data store that can be updated only by the statechart itself.

You can pass a machine its context using the context property:

const machine = createMachine({
context: {
count: 0,
},
});

Next, we’ll see how to update the context using the assign action.

Assign action

Assigning new values to the context in XState is done through the assign action and is the only way to change a machine’s context. Never mutate a machine’s context externally. Every context change should happen explicitly due to an event.

The assign action takes the context assigner, representing how values should be assigned in the current context. The assigner can be an object:

import { createMachine, assign } from 'xstate';

const machine = createMachine(
{
// adding a schema for the events will make them typesafe
schema: {
events: {} as { type: 'INCREMENT'; value: number; time: Date },
},
context: {
count: 0,
updatedAt: new Date(),
message: 'Hello World',
},
on: {
INCREMENT: {
actions: 'assignToContext',
},
},
},
{
actions: {
assignToContext: assign({
// increment the current count by the event value
count: (context, event) => context.count + event.value,

/*
* you can update multiple properties at once
* we name the context parameter `_`,
* to indicate that we don’t use it
*/
updatedAt: (_, event) => event.time,

/*
* to keep TypeScript happy,
* update using a function with the context parameter
* again we use the name `_` to indicate that the
* parameter is unused
*/
message: (_) => 'Count changed',
}),
},
}
);

Or the assigner can be a function that returns the updated state:

import { createMachine, assign } from 'xstate';

const machine = createMachine(
{
context: {
count: 0,
message: '',
},
on: {
INCREMENT: {
actions: 'assignToContext',
},
},
},
{
actions: {
assignToContext: assign((context) => {
return {
count: context.count + 1,

// assign static value to the message (no function needed)
message: 'Count changed',
};
}),
},
}
);

You can pass several assign actions in an array and they’ll be executed sequentially:

// ...
actions: [
assign({ count: 3 }),
// context.count is now 3

assign({ count: context => context.count * 2 })
// context.count is now 6
],
// ...

Using context in actions

When XState fires an action, the action receives several arguments. The first argument is the current context of the machine. The second argument is the most recent event sent to the machine.

import { createMachine } from 'xstate';

createMachine(
{
context: {
count: 0,
},
on: {
LOG_COUNT: {
actions: 'logCountToConsole',
},
},
},
{
actions: {
logCountToConsole: (context, event) => {
console.log(`Count is ${context.count}`);

console.log(event.type); // Logs 'LOG_COUNT'
},
},
}
);

Summary

States are used for handling your apps states which you know about in advance. Context is a data store that you can use to store any arbitrary values. The assign action can be used to assign values to the context, and the context can be used in any action you call.