Conditionally Transition to States with Guards in XState

Kyle Shevlin
InstructorKyle Shevlin
Share this video with your friends

Social Share Links

Send Tweet

Not all transitions should be taken immediately. Occasionally, we would like to conditionally take a transition. We do this through the use of "guards". A "guard" is a predicate function (a function that returns a boolean) that is set on a transition object's cond property (short for "conditional").

When an event is sent to the machine and it encounters a transition object with a cond property set to a guard function, it will call that function with the current context and event object. If the guard returns true, the transition will be taken, otherwise, it will attempt the next transition for the event, or remain in the current state.

//...
EVENT_NAME: {
  target: 'state-name-of-transition-target',
  cond: (context, event) => predicateFunction(context, event)
}

Instructor: [00:00] Here I have a state machine for a vending machine. It has two states, idle and vending. On idle, I can select an item which currently targets vending as the transition.

[00:10] However, that's not how we want our vending machine to work. We don't want them to just be able to select an item without having paid for it. We can do this by adding a guard.

[00:20] To start, we're going to change this from being a string to an object of target vending, since it's still the state that we want to target. Next, we're going to add the cond property. The cond property is a predicate function. That's a function that returns a Boolean.

[00:36] In this case, we want it to return true when we'd like to take the transition and return false when we don't want it to. Guard functions receive the context and event as arguments. However, in this case, we only need the context.

[00:49] We want it to return true when context.deposited is greater than or equal to 100. We're going to update our machine. We'll see that a cond has been set on select item. In fact, it's disabled. We can't select it right now.

[01:03] Now, another way to set the condition is rather than setting the function here in place, we can use the second argument to machine, the options object and add guards to that.

[01:13] I am going to add a guard down here. We'll create a new conditional function. We'll call it depositedEnough. We'll take the function that we wrote up here. We're going to cut that out and replace it with a string of the same method name, so depositedEnough, and take that function and place it here.

[01:36] We're now going to update our machine. We'll see that the visualization has updated to show us that the depositedEnough condition is here. It's also red because it hasn't been met yet.

[01:46] Let's open up the state tab. We'll be able to watch the context increase as I deposit quarters. One, two, three, four. DepositedEnough is now green. That condition is true and I can take this transition by firing this event.

[02:01] I select my item and it gets vended.