Check out a free preview of the full State Machines in JavaScript with XState course:
The "Assignment Action Exercise" Lesson is part of the full, State Machines in JavaScript with XState course featured in this preview video. Here's what you'd learn in this lesson:

Students are instructed to use context to update the extended state of the drag/drop state machine.

Get Unlimited Access Now

Transcript from the "Assignment Action Exercise" Lesson

[00:00:00]
>> In Exercise 05, we are going to be working on fleshing out this drag and drop. This is probably gonna be one of the harder exercises to do, because there's a lot of things we need to keep track of. But first, let's think about how drag and drop sort of works.

[00:00:18] At first, something, or whatever we're dragging, is in an idle state. And in that state, nothing happens. But once we have mousedown, we're recording where on that object it was clicked. So in this case, it was clicked a little bit off center. And so we're going to assign that to px and py.

[00:00:38] And then when we drag, we're going to be listening for the mousemove event. And so whenever we get a mousemove event, we don't change state, because we're still in dragging. But instead, we're going to be updating dx and dy. And then once our mouse is up, we are going to set x and y to those new values, so that we could place that element where it needs to be.

[00:01:06] And so to do this, we're going to be doing three actions. The first is assignPoint, in which we assign those px and py values from the initial event.client x and event.client y. While we're dragging that mousemove event, we're gonna be assigning the Delta. And the Delta is just that px value minus the events.client x value.

[00:01:31] So we want to know how far it traveled from the initial place we clicked. And same thing with py and events.client y. Now when we go to mouseup, we want to do something different. We want to assign the x value to the Points.x, plus how far it's traveled.

[00:01:51] So we want to know, okay, now it's over here. Or sorry, not the px, but the original x value, to how far it's traveled. And then we want to assign the y to the same. So that's why we're keeping track of the Delta, because once our mouse is up, we need to move those x and y values by those dx and dy values.

[00:02:13] And so that's going to be Exercise 05. Okay, so Romaine asks, why should we use assign actions instead of mutating context directly? And that's a great question. Sometimes you might be tempted to mutate context directly, but this is going to be important later on, too. You want to make sure that when you're updating context, you do so based on an event.

[00:02:43] If you could just mutate context, then that could happen at any time, whether an event happens or not. So we're not using the model where we do a mutation undetected. That's not the model of state machines or state charts. Instead, state machines and state charts say everything happens for a reason.

[00:03:01] And that reason is because an event occurred. And by keeping pure, we could also have sort of a similar Redux, NgRx, ViewX set up, where we could record the previous states and keep track of what those previous contexts were. Of course, don't worry, it's garbage collected. So only if you really want to record what the previous states were, you will have that, including the context in which that previous date occurred.

[00:03:31] But in general, do not mutate context, especially because that might have unintended side effects. The machines context is just a normal object. So if you accidentally mutate that, you're going to be mutating every single service instance that uses that machine, which is not a good idea. All right, so this is Exercise 05, which is context.

[00:03:56] So what we're going to be doing is we're gonna be using context to update the extended state of our drag and drop machine. We're gonna be having three assignments, which is, we're assigning the point values. Which is when you initially click. We're assigning the Delta values, which is how far away from those original point values the mouse has traveled in a mousemove transition.

[00:04:17] And we're going to be assigning the resting position, once our mouse is finally up. And so start with inline assign action objects. So this assign, refactor these out of the machine, once you have them working. And then you could set those as configurable actions, if you have time.

[00:04:40] So let's just, I'm just gonna show you. Over here, we have a clue to what the initial context should be. We have our x and y values, which represent the current position of the box. We have the dx and dy values, which represent how far it's traveled while we're dragging.

[00:04:57] And we have the px and py values, which represent where it was originally clicked. So it doesn't do any jarring motion while you drag it. And so, again, what you're going to wanna do is you're going to wanna set that context. So you set that in here, and then you want these assign actions to occur.

[00:05:19] One important note about assign, assign is a pure function, assign doesn't do anything. It returns an object. So no side effects are happening in the assign action. In fact, if we say, let's actually log this. So console.log, and let's say, (assign({name: 'David',. So when we log this out, we are going to see, let's go to Exercise 05.

[00:05:56] We're going to see that it's a plain object. It has type of xstate.assign, and it has an assignment. So its name, David. And so if we have count: (context) => context.count + 1. Remember, this is being defined outside the machine. Then we have two assignments. One of them is for name, and another one is for count, which is that function.

[00:06:23] So assignment does nothing, actually just knows how to take this assign action and apply it to the machine. And it does that when you transition the machine. So again, it's a pure function. It returns an object, and xstate knows what to do with that object.