Hi David,
We are making use of final states but we are now wondering why it's not possible to have multiple final states because there is only one onDone transition in the parent FSM.
In our project, we often have two ways of exiting our nested FSMs, after a successful processing, or after a failed processing. Unfortunately, we can only use onDone for one of these cases and the nested FSM needs to know which event the parent FSM is expecting to exit.
Here is a simple example:
describe('multiple onDone needed', () => {
const nestedFsm: MachineConfig<DefaultContext, any, EventObject> = {
key: 'nestedFsm',
initial: 'VALIDATING',
states: {
VALIDATING: {
on: {
VALID_EVENT: 'VALID',
INVALID_EVENT: 'INVALID'
},
onEntry: 'ENTER_VALIDATING'
},
VALID: {
onEntry: 'ENTER_VALID',
type: 'final'
},
INVALID: {
onEntry: 'ENTER_INVALID',
type: 'final'
}
}
};
const parentFsm = Machine({
key: 'parentFsm',
initial: 'VALIDATION',
states: {
VALIDATION: {
onEntry: 'ENTER_VALIDATION',
onDone: 'SUCCESS',
...nestedFsm
},
SUCCESS: {
onEntry: 'ENTER_SUCCESS'
},
FAILURE: {
onEntry: 'ENTER_FAILURE'
}
}
});
describe('multiple onDone', () => {
it('test multiple onDone', () => {
const nextState = parentFsm
.transition({VALIDATION: 'VALIDATING'}, 'INVALID_EVENT');
const actual = nextState.actions.map(a => `${a}`);
const expected = ['ENTER_INVALID', 'ENTER_FAILURE'];
assert.deepEqual(actual, expected);
});
});
});
In this example, I would like both VALIDand INVALIDstates to be final, and that only the parent FSM would know how to transition to SUCCESS or FAILURE states.
Any thoughts?
This is a complex part of the SCXML spec 馃槄 But I'm almost done implementing it! (see referenced commit 3348a9b)
The way this will work is exactly the same way you would expect it to work when interacting with external systems, i.e., the type of "done" response depends on the data received. For instance, when you make an API request, you might get { status: 200, ... } or { status: 500, ... } which refer to some success or failure state, respectively.
So in the same way, data will be used on the type: 'final' states as the payload for the onDone event:
VALID: {
type: 'final',
data: { status: 'ok' }
},
INVALID: {
type: 'final',
data: { status: 'error' }
}
// ...
// parent state
VALIDATION: {
...nestedFsm,
onDone: [
{ target: 'SUCCESS', cond: (_, e) => e.status === 'ok' },
{ target: 'FAILURE', cond: (_, e) => e.status === 'error' },
{ target: 'UNKNOWN' }
]
}
This is working in the master branch and will be out in the next release.
Great news, thanks a lot for your quick feedback!
Can't wait to try it out 馃
Added to docs in master, will go out 馃敎
https://xstate.js.org/docs/guides/communication.html#done-data
Most helpful comment
This is a complex part of the SCXML spec 馃槄 But I'm almost done implementing it! (see referenced commit 3348a9b)
The way this will work is exactly the same way you would expect it to work when interacting with external systems, i.e., the type of "done" response depends on the data received. For instance, when you make an API request, you might get
{ status: 200, ... }or{ status: 500, ... }which refer to some success or failure state, respectively.So in the same way,
datawill be used on thetype: 'final'states as the payload for theonDoneevent:This is working in the
masterbranch and will be out in the next release.