I've been looking through the docs & can't find any examples of calling a transition from an action. Example, if an action contacts a server for a user to sign in, I'd want to transition to ethier signedIn or error state based on the response:
const signInMachine = Machine({
id: 'signIn',
initial: 'dataEntry',
strict: true,
states: {
dataEntry: {
on: {
SUBMIT: {
target: 'awaitingResponse',
actions: ['contactService']
}
}
},
awaitingResponse: {
on: {
SUCCESS: 'signedIn',
FAIL: 'error'
}
},
signedIn: {},
error: {}
}
}, {
actions: {
contactService: (ctx, evt) => {
myContactServerFunc()
.then(() => transition to signedIn state)
.catch(() => transition to error state)
}
}
})
I have seen examples of pulling out a send method from the library:
import { Machine, actions } from 'xstate'
const { send } = actions;
However this does not work.
Right, actions are the wrong approach for this. When you execute an action, it should be considered _fire and forget_; in other words, it's a side-effect that does not affect the state of the machine.
Instead, what you want is to invoke a service, since this offers a "...more tightly coupled form of communication" with the parent statechart (see https://www.w3.org/TR/scxml/#ExternalIntroduction for info regarding <invoke> in the SCXML spec).
For your example (and I'll add this to the docs), it would be:
dataEntry: {
on: {
SUBMIT: 'awaitingResponse'
}
},
awaitingResponse: {
invoke: {
// function that returns a promise
src: (ctx, event) => myContactServerFunc(),
onDone: 'signedIn', // .then(), resolved
onError: 'error' // .catch(), rejected
}
},
signedIn: {},
error: {}
This is working in master and will be out in the next release.
By the way, you can also write it in a JSON-compatible format:
invoke: {
src: 'contactServer',
onDone: 'signedIn',
onError: 'error'
},
// ...
// second argument to Machine()
{
services: {
contactServer: (ctx, event) => myContactServerFunc()
}
}
Invoking promises example: https://xstate.js.org/docs/guides/communication.html#invoking-promises
Most helpful comment
By the way, you can also write it in a JSON-compatible format: