Trigger Side Effects in a Zag.js State Machine with the watch Property

Segun Adebayo
InstructorSegun Adebayo
Share this video with your friends

Social Share Links

Send Tweet

Let's actually implement inputting values into our Pin input. We have 3 functions that need implemented. They are setFocusedValue, focusNextInput, and executeFocus. The first two will be pretty straight forward in grabbing values from context and our events and setting new values to our context.

When we finish implementing setFocusedValue and setFocusedInput we'll need a way to actually trigger our focus to be set in the DOM. This is where the watch property in a Zag.js State Machine comes into play. We will watch the focusedIndex context value and run executeFocus any time the focus index changes in our machine (e.g. when focusNextInput is triggered after an input event).

Instructor: [0:01] Now, let's implement the input event in the focus state. We're going to move the app over to the side so we can actually see what's coming in from the input itself.

[0:13] Right here, when the change event happens, we're sending in the index and the value as part of the event. On here, the two actions that we're running is setFocusValue and focusNextInput. Before we write the implementation, one thing we need to update here is the value.

[0:32] We know that there are four inputs in this example. Instead of using an empty array that's just there, we can create an empty array that includes strings for the four inputs. Here I can say array.from and I can put a length of four. I can fill that all with an empty string.

[0:50] TypeScript yields out me here. What I can do to fix that is to cast that to a string. Now we have an empty array of four different values that are empty strings in there. Let's go over here to the implementation.

[1:06] We start with set focus value. Set focus value gives us access to the context and the event. What we want to do here, we know that we need the focus index and we want to set the value at that index. The implementation for that would be, we need to set the value at context.focus index and we need to set that to event.value.

[1:35] Again, here we have access to the index and we have access to value. We could also replace this with event.index. Because this function is already called set focus value, we want to make sure that we use the focus index. The next thing we want to implement is the focus next. I'm just going to put here focus next input, grab the context and the event.

[2:00] To be able to focus the next input, we want to get the index of the current input. Then we do index plus plus to increment that to the next index. Then we want to focus the next input, which is where we do that execution there. Note that we currently have the focus indexin the context. Instead of focusing the next input, what we could do is basically set the focus index.

[2:28] We would execute the side effect later on to actually do the focusing for that DOM element. To implement this, what we do here is we create a variable for the next index and we set this to be the minimum of the context.focus index plus one and the context at value.length. This way, we actually make sure that the value never actually overflows the given range.

[3:00] Now once we have that in place, we set the focus index to be equal to the next index. That solves the focus next input action. To verify that this works, let's switch over to the UI and see what happens there. As you can see, the value is set to the default empty strings for the four different inputs there.

[3:23] If I focus here and I type A, from here you can see that the event is sent. The first value in there is A. The focus index has been incremented to one, which means we are actually supposed to move the focus to the next one. That's exactly what we are going to implement now. To fix this, we are always setting the focus index. As a side effect of setting the focus index, we actually now want to execute focus for those inputs.

[3:53] Let's write an action to actually execute focus. We're going to create the action here, execute focus, that takes the context. In here, given the DOM structure, we have here the input group. We can query all the inputs directly from that input group. Here we're going to grab the input group element by executing a document query selector. Then we grab the data part of the input group.

[4:29] Now that that is set in place, we can do an early return if we don't have the input group. Now within the input group, let's get all the input elements. We pull input elements and then we can call from the input group to a query selector all for all the data parts that is equal to input. Because this returns a node list, what we need to do then is to wrap this within an array.from.

[5:05] Now we actually get an array of all the input elements. Now to improve the type, we can come here and do HTML input element. That should now give us an array of input elements. Now we create a variable for the input to focus. The input element we really need to focus on is the one that matches the context focus index. Here again, maybe we just rename this to input to keep this for short. Now we can do input.focus. That way we actually execute the focus.

[5:45] Now that we have created the action, let's figure out, how do we connect setting the focus index with actually executing focus here? Thanks to ZagJS, we have a property called watch which allows you to define side effects for the context values. As a side effect here, we say that when the focus index changes, we're going to execute focus. That is how we instruct the machine to actually carry out side effects.

[6:16] Anytime we set the focus index, we automatically execute focus. Now, one thing you might notice here right away is the fact that the focus index starts with minus one. Here we are trying to actually access the focus index of the input element, which could potentially be minus one. As another guard in here, we can say when the context focus index is minus one, we just avoid querying the DOM.

[6:43] We just save all that work and only do it when the focus index is a valid value. Let's switch over to the UI and see what it looks like. Now if I focus on the first field and press A, you can see that focus moves to the next field automatically. If I press B, it moves, if I press C, it moves, and so on. If I press R, you see that it actually ends there, which is pretty neat.

[7:08] Now the thing here is we most likely have an overflowing value here, because the focus index is set to four and that should not be the case. At the maximum, we can go from zero base index to zero, one, two, and three. Let's come right here and just fix this, fix the condition that we've got here.

[7:27] To fix this, we can just subtract one here from the value length and then head back here again and try again. We type A, B, C, D. Here you see that the focus index stays at three, which is the desired behavior.