State
instance
- 🆕
state.changed
property indicates whether a state has changed from a previous state. A state is considered changed if:- its
value
is different from the previous statevalue
- its
value
is unchanged but there areactions
to be executed on the new state
- its
- 🆕
state.nextEvents
property represents all next possible events from the current state. - 🆕
state.matches(parentStateValue)
is equivalent to thematches()
utility function, in that it determines if the current state matches the provided parent state value. - 💥
state.actions
now returns an array of action objects instead of plain strings, with at least two properties:type
- the action type (string)exec
- the action implementation function (if it is defined).
Parallel states
- 🔧 Flat nested parallel states are now representable in the state value, as empty objects (
{}
). - 💥 Using
parallel: true
is deprecated; usetype: 'parallel'
instead.
History States
- 🆕 History states are explicitly defined with
type: 'history'
andhistory: 'parallel'
orhistory: 'deep'
(parallel by default). See https://xstate.js.org/docs/guides/history/ for more details. - 💥 The magic
'$history'
string is deprecated.
Final States
- 🆕 Final states are now implemented and specified with
type: 'final'
. See https://xstate.js.org/docs/guides/final/ for more details.
Machine
- 🆕
machine.withConfig(...)
lets you override configuration options in the original machine, as a new machine instance:
const customMachine = someMachine.withConfig({
actions: {
something: () => console.log('overridden something action')
},
guards: {
someCondition: () => true
}
});
Invoke
- 🆕 Declaratively invoke other statecharts (or plain Promises) using the
invoke
method. See https://xstate.js.org/docs/guides/communication/ for more details.
Interpreter
- 🆕 There is now an (optional) interpreter shipped with
xstate
. See https://xstate.js.org/docs/guides/interpretation/ for more details.
Assign and context
- 🆕 Machines can be defined with a
context
, which is the extended state of the machine. - 🆕 The
assign()
action allows you to update the context declaratively. See https://xstate.js.org/docs/guides/context/ for details.
// increment a counter
{
actions: assign({ counter: ctx => ctx.counter + 1 })
}
Delayed transitions and events
- 🆕 Delayed transitions can be defined on the
after: ...
property of a state node config:
{
after: {
1000: 'yellow'
}
}
- 🆕 Delayed events can be defined as an option in
send()
:
actions: send('ALERT', { delay: 1000 });
See https://xstate.js.org/docs/guides/delays/ for more details.
Actions and Events
- 🆕
send(event, { to: ... })
allows you to specify the recipient machine for an event. See https://xstate.js.org/docs/guides/communication/#sending-events for more details. - 🆕
sendParent(event, options)
similarly sends an event to the parent statechart that invoked the current child machine. - 🆕
log(expr, label)
declaratively logs expressions given the currentcontext
andevent
. - 🆕
after(delay, id)
creates an event for the given state nodeid
that represents a delay ofdelay
ms. - 🆕
done(id, data)
represents that the parent state node with the givenid
is "done" - that is, all its final child state nodes have been reached. See https://xstate.js.org/docs/guides/final/ for more details. - 🆕
error(data)
represents an execution error as an event.
Breaking changes
IDs are recommended on the root state node (machine):
const machine = Machine({
id: 'light',
initial: 'green',
states: { /* ... */ }
});
This syntax will no longer work:
// ⚠️ won't work in v4
const machine = Machine({
// ...
states: {
green: {
on: {
TIMER: {
yellow: { actions: ['doSomething'] }
}
}
}
}
});
You now specify the transition as an object (or an array of objects) instead:
// ✅ will work in v4
const machine = Machine({
// ...
states: {
green: {
on: {
TIMER: {
target: 'yellow',
actions: 'doSomething' // notice: array not necessary anymore!
}
}
}
}
});
Simple transitions as strings still work:
// ✅ still good
const machine = Machine({
// ...
states: {
green: {
on: {
TIMER: 'yellow'
}
}
}
});
When specifying types of state nodes, use type
:
- parallel: true,
+ type: 'parallel'
- history: true,
+ type: 'history',
+ history: 'deep', // 'shallow' by default
And every property that takes an array now optionally takes an array if you have a single element:
{
actions: ['doSomething']
// You can do this instead:
actions: 'doSomething'
}
Which is purely for convenience. That's about it!