The basic syntax for invoking a child machine is the same as for invoking promises or any other service:
// ...
current_state: {
invoke: {
src: childMachine,
data: {}, // optional initial context
onDone: {
target: 'next_state',
actions: 'someAction',
},
onError: {
target: 'error',
actions: 'printError',
}
},
},
When you invoke a promise, an error thrown inside will automatically trigger the onError
block. You could expect the same thing to happen when invoking a child machine and throw an error like so:
// ...
states: {
fetch: {
invoke: {
src: 'fetchSomething',
onDone: { target: 'upsert', actions: 'someAction' },
onError: {
actions: (_, event) => {
throw event.data
},
},
},
},
upsert: {
// ...
},
success: {
type: 'final',
},
},
The problem
However, this doesn’t work, and the “Invoking services” docs only mention sendParent
as a way to communicate from child to parent machine.
You could use sendParent
to solve the issue, but you’d need to send a custom event from your child machine and handle it in the parent, outside of the onError
block:
// ... child machine
actions: {
throwError: sendParent((_, event) => ({
type: 'CHILD_ERROR',
data: event.data,
})),
},
// ... parent machine
current_state: {
invoke: {
src: childMachine,
data: { /* ... */ },
onDone: { /* ... */ },
},
on: {
CHILD_ERROR: {
target: 'error',
actions: 'printError',
},
},
},
That’s actually what I did until a friendly discord user pointed me in the right direction.
The solution
Turns out the answer was in the “Actions” page of the docs, under the escalate
action, which “escalates an error by sending it to the parent machine.” Exactly what we need.
Here’s how you’d refactor the throwError
action so that it would trigger the onError
block of the parent:
// ...
throwError: escalate((_, event) => ({
data: event.data,
})),