Question
Hi David, I have enjoyed your youtube videos, and have started investigating xstate for use in my project. It was a breeze to implement the traffic light example, and step through the states with a button. Thank you! Hat Tip for the _Ryan Florence and the State Machine_ reference 👍
For my use case, I have a question.
Is it currently possible to determine what ACTIONS are defined for a particular state?
i.e. on the review state, I can determine the actions SUBMIT and REJECT are available?
I can then show these as actions on my form.
{
key: "employment",
initial: "draft",
states: {
draft: {
on: {
SUBMIT: "review"
}
},
review: {
on: {
SUBMIT: "manager",
REJECT: "draft"
}
},
manager: {
on: {
APPROVE: "complete",
REJECT: "review"
}
}
}
}
I think what you are referring to are EVENTs in xstate terms.
Actions are explained here: http://davidkpiano.github.io/xstate/docs/#/api/actions
Back to your question:
I think there are multiple solutions for your problem.
You could potentially just access the keys on your state with Object.keys(stateMachine.states.draft.on)
There is also a graph utils package which exports a getEdges fn which should also contain the events but you probably need another step to extract the actual events for your state.
import { getEdges } from 'xstate/lib/graph';
maybe someone else has a better idea
You can get all events for a machine via:
myMachine.events; // ['SUBMIT', 'REJECT', ...]
You can get then for a specific state node via the same:
myMachine.states.draft.events; // ['SUBMIT']
You can also see if a state node (or machine) handles a certain event:
myMachine.handles('SUBMIT'); // true
Brilliant! Thanks so much.
Is it worth me adding this to the doco, or have my noob eyes not read it correctly?
Is it worth me adding this to the doco
It is!
Trying to do the same - need to get a list of the transitions that can occur on a node. These are known as EVENTS here, yes?
state.events seems to always just be an empty array...
@davidraine David K's guidance above worked a treat for me. I created a simple app that cycles through the various states, showing buttons of the EVENTS available at a given state. Perhaps it will assist with your question. https://github.com/seankellyaus/xstate-implementation
Thank you - that is really helpful.
I am still struggling to work out what events are available. I have a tree that looks like this:
inactive
do login -> login
login
close -> inactive
get login details
submit -> send login
send login
received token -> inactive
The issue is that when I go to the "get login details" state, the events that should be available to me are "submit" and "close", but not "received token". The "close" event is part of the parent and is in scope, but the "received token" is not.
How do I get a list of the events that are in scope? I can see no way to dig these out. In sketch it is possible using the approach below, but that uses a completely different internal model, of course.
let transitions = model.active_states[0].transitions.concat(model.active_states[0].parent.transitions);
@davidraine (et. al.) The future API for this in v4 will be:
const nextState = someMachine.transition(...);
// get next possible events for the given state
nextState.nextEvents;
// => ['FOO_EVENT', 'BAR_EVENT', 'BAZ_EVENT']
The .nextEvents property holds all the possible next events for the given _state value_; that is, all events that can be executed from the state nodes that represent the state value in the given state, excluding forbidden events. So if you have an event that should be "forbidden":
const myMachine = Machine({
// ...
states: {
foo: {
on: { FORBIDDEN_EVENT: undefined }
}
}
});
Then FORBIDDEN_EVENT will not be returned in .nextEvents when you're in the foo state.
See 5240c3e for more implementation details.
Great work @davidkpiano, v4 is awesome!
state.nextEvents is in master and will be released in 4.0.
Hi,
state.nextEvents returns available transitions for a given state, but its seems to not run the guards.
Is there a better way than this ?
machine.initialState.nextEvents.filter(next => machine.transition(machine.initialState.value, next).changed);
This allows events which leads to the same state:
machine.initialState.nextEvents.filter(next => machine.transition(machine.initialState.value, next).tree.isResolved);
state.nextEventsreturns available transitions for a given state, but its seems to not run the guards.
Guards are dependent on context and event data, so they will not run on plain events - this is by design.
You can do this:
currentState.nextEvents.filter(nextEvent => {
return machine.transition(currentState, { type: nextEvent, /* ... */ }).changed;
});
Thanks, that matchs the exemple i've given.
My second second exemple returns also an resolved event where changed is not triggered.
This might be helpful for a more "workflow" approach.
Why not push them into the documentation if it's valid?
In the future we might improve SCXML compatibility and nextEvents API might get cimberse with that as in SCXML transitions might work on event type prefixes so its impossible to give a user true possible „nextEvents” as that set wont be fully known by XState
Most helpful comment
You can get all events for a machine via:
You can get then for a specific state node via the same:
You can also see if a state node (or machine) handles a certain event: