I have an xstate state machine with 3 states. When data is fetched and final state isreached, the state machine is 'Done'. I would like to go back to initial state of idle after 'finally completing the task'. How do I accomplish that? For example,
states: {
idle: {
id: 'initialState'
},
waitingForA: {
invoke: { /*Promise*/ },
onDone: { target: 'wiatingForB' },
onError: { alert(); }
},
waitingForB: {
invoke: { /*Promise*/ },
onDone: {
target: 'waitingForC',
actions: assign({ bReturnCode: (context, event) => event.data, })
}
},
waitingForC: {
invoke: { /*Promise*/ },
onDone: {
target: 'success',
actions: assign({ cReturnCode: (context, event) => event.data, })
},
onError{
target: 'showAlert'
}
},
success: {
type: 'final' //here, I would like ot go back to idle state;
},
final: {target: 'idle'}
}
}
You can use an always
transition which transitions to the idle
state without the need to explicitly fireing an event. Just keep in mind that the success state is not allowed to be a final state otherwise the execution of the service would stop.
You can read more about these automatic ('eventless') transitions in the XState docs here.
success: {
// is not allowed to be a final state
// because you still want to transition to idle
// and the execution of the service would stop
always: 'idle',
},
id: 'some-machine'
predictableActionArguments: true,
context: { bReturnCode: null },
initial: 'idle',
states: {
idle: {
id: 'initialState',
on: {
start: 'waitingForB'
}
},
waitingForA: { /* ... */ },
showAlert: { /* ... */ },
waitingForB: {
invoke: {
"id": "getStuffC",
"src": (context, event) => debugFetch,
onDone: {
target: 'waitingForC',
actions: assign({ bReturnCode: (context, event) => event.data })
},
onError: {
target: 'showAlert'
}
},
},
waitingForC: {
invoke: {
"id": "getStuffC",
"src": (context, event) => debugFetch,
onDone: {
target: 'success',
actions: assign({ cReturnCode: (context, event) => event.data })
},
onError: {
target: 'showAlert'
}
},
},
success: {
// is not allowed to be a final state
// because you still want to transition to idle
// and the execution of the service would stop
always: 'idle',
},
}
function debugFetch() {
return new Promise((resolve, reject) => {
const delayInMs = 1000
setTimeout(() => {
if (Math.random() <= 0.01) {
reject('some error')
}
resolve('success')
}, delayInMs)
})
}