Skip to content
Version: XState v4

Machines

Callback actors are one way to start invoking long-lived actors and are useful for simple to intermediate cases. But sometimes, you’ll want to use all the power of a statechart.

Machine actors are valuable for such use cases. You can pass a machine directly to the invoke src property to invoke the machine in that state.

import { createMachine } from 'xstate';

const childMachine = createMachine({
/* ... */
});

const parentMachine = createMachine({
invoke: {
src: childMachine,
},
});

Just like with callback actors, the child machine is stopped if the state where it’s invoked is exited.

Machine actors enable you to create machines that can act as reusable modules across your application.

sendParent

Child machines can communicate to the parent that invoked them via the sendParent action.

import { createMachine, sendParent } from 'xstate';

const childMachine = createMachine({
after: {
3000: {
actions: sendParent({
type: 'TIMER_DONE',
}),
},
},
});

const parentMachine = createMachine({
initial: 'waiting',
states: {
waiting: {
on: {
TIMER_DONE: {
target: 'complete',
},
},
invoke: {
src: childMachine,
},
},
complete: {
type: 'final',
},
},
});

In the example above, the parentMachine invokes the childMachine in the waiting state. Three seconds after being started, the childMachine sends the TIMER_DONE event to its parent. The TIMER_DONE transition in the parentMachine puts the machine into the complete state.

Only use sendParent on child machines

If the child machine doesn’t have a parent, for instance, if the machine is run with interpret, then sendParent will throw an error.

onDone in child machines

When a child machine reaches a final state, it reports that it’s done to its parent. The parent can listen for this event with the invoke.onDone property:

const childMachine = createMachine({
initial: 'waiting',
states: {
waiting: {
after: {
3000: {
target: 'complete',
},
},
},
complete: {
type: 'final',
},
},
});

const parentMachine = createMachine(
{
invoke: {
src: childMachine,
onDone: {
actions: 'logComplete',
},
},
},
{
actions: {
logComplete: () => {
console.log('Child machine complete!');
},
},
}
);

You can also send data along with this onDone event. This “done data” is specified on the final state’s data property:


const secretMachine = createMachine({
initial: 'wait',
context: {
secret: '42',
},
states: {
wait: {
after: {
1000: { target: 'reveal' },
},
},
reveal: {
type: 'final',
data: {
secret: (context, event) => context.secret,
},
},
},
});

const parentMachine = createMachine({
context: {
revealedSecret: undefined,
},
invoke: {
src: secretMachine,
onDone: {
actions: assign({
revealedSecret: (context, event) => {
// { type: 'done.invoke.<id>', data: { secret: '42' } }
return event.data.secret;
},
}),
},
},
});