State Modeling in React with XState Spawning Actors Solution
Transcript from the "Spawning Actors Solution" Lesson
>> Okay, so let's talk about how we could spawn actors. And remember spawning is very useful for if you have a dynamic number of actors, which might not be within the current state. So basically these actors, once they're spawns, they're alive until they are explicitly stopped or the machine that they were created in stops.
[00:00:26] And so in this case, because we're dynamically creating these timers at any time we want them to be dynamic. So spawning actually makes a lot of sense. So we're going to spawn a new timer machine here. And so we could say const timer equals spawn and we're gonna create a timer machine and that's gonna take a duration.
[00:00:52] So, on the add action, we see that this is the wrong exercise. Okay. So, we see that there, that we are going to send an add event with a duration. And that duration is gonna be a custom duration. So that comes from events. So we'll put events dot duration over here.
[00:01:19] And this is gonna be our newly spawned timer. So we're going to return The timers is gonna be the context dot timers with that timer concatenated on the end. So concat timer. And I use concat because it basically makes an immutable updates and creates a new array with that added timer and so the current timer, we have to think a little bit about this one because the length of the context dot timers is how many timers there already are.
[00:01:57] And when we add the timer, that's going to be added to the end. So, since we're adding that to the end, we could just say context dot timers dot length. In other words, if there's already three timers, then the length is three, but the index of the last one is two.
[00:02:17] So if we add that timer now that timer is going to be in the third index, even though the length is four, so hopefully that makes sense. So now once we do that, let's uncomment this so that we get to the timer and hopefully we will see that the timer is added successfully which it is and we could dynamically add a number of timers to or we could cancel and go back to the timer's that we already have.
[00:02:53] Delete. [COUGH] And there we go. So were there any questions about this exercise?
>> All right, so quick one that the state is immutable like when we have the different states we're in but for the context it can be it's mutable, right? Like the I know you're returning it in like a Redux like fashion where you're, getting a new array but it is a mutable portion of the state.
[00:03:27] So that's a really good question. The question was about can basically can context be mutated inside of this assign? And the short answer is no, or at least you shouldn't. And that's because when you have a state and we'll go ahead and console log dot state, so console dot log state just show you right over here, nor that error.
[00:03:58] All right, so when we have this, we have a state object and that state object is going to also have a previous state which is in history. And so history is going to contain the very last states before this new state happened. And that might have been context that is different than the current states.
[00:04:20] If we mutate context, then that mutates the same context object both for that previous state and the current state. So it might make it really difficult to do things such as compare what changed in context. So in general, you don't want to mutate context directly. You want to return an immutable copy of context.
[00:04:43] Xstate does help you out here though. When you have an assign action, you could tell it which property of the context object that you want to assign. And this lets you avoid doing things like spreading the current context and only updating that property that you want to update.
[00:05:03] So it makes it a little bit easier to do that. Now there are libraries such as Immer which makes it a lot easier to update contexts in an immutable way, even though it really looks like mutation and there is actually a library ot a package called Xstate Immer.
[00:05:27] And this allows you to grab that assign. And since it's basically using immer in the background, you could update the context as if it were mutable. So over here, for example, I'm updating context dot count by doing plus plus which is a mutable action. But because we're using assigned from Xstate immer, it's actually an immutable update.
[00:05:54] So you're still safe with that one. But yeah, in general, try to, it should really be a rule be pure and have your assign function be pure so that it doesn't do any side effects. Except spawning, that could cause the side effects but that's sort of built into Xstate, so don't have to worry about that too much.
[00:06:23] But yeah, try not to mutate context itself. You will notice that it will work and it will continue to work in version 5, if you accidentally do it but there might be some bugs and glitches if you decide to do that because Xstate assumes that context is immutable or rather it should be immutably updated.
[00:06:49] So hopefully that answers your question. Yeah, and typically do the way you're saying keep things pure. I was just tinkering with it and I saw that you could see that set.
>> Yeah. Yeah. So again, you can mutate context and it technically will work. I'm not gonna encourage it, but it will work.
>> Okay, I
>> Yeah, there's something interesting with the actor model because actors have their own local private state. What you can also do there is it doesn't matter if that local private state is mutated or not. I'm talking about the actor model in general, but you could totally mutate and do whatever you want to that local private state.
[00:07:32] It's just that that state should be immutably shared. Meaning if you have an object in there and you mutated somehow, and you want to share that object with another actor immutably, then you need to create a copy of that mutated object and send it over. So, locally you can mutate it, but anything external make sure it's immutable.
[00:07:56] And that's why a good rule of thumb is just make sure everything's immutable so you don't have to figure out what's allowed, what's able to be mutated and what's not. So, yeah.
>> I guess this is like a small follow up but, I saw the project's written in TypeScript, I haven't dived into the source code too much.
[00:08:17] But I imagine the signature of where you have context and event there, you could just mark context as read only and then, at least in TypeScript lan it would say hey, you can't change that. And then you're kinda forced to return a new context.
>> Right. So the question was about in TypeScript, how our context and events typed.
[00:08:40] Context is not typed as read only over here. That's something that I should probably change just to help with anyone using TypeScript just so that they don't accidentally mutate context. But yeah, it should be assumed that everything here is read only.
>> Okay, thanks.
>> Any other questions?
>> You mentioned immer and I played around with that and got it working pretty easily. And it's definitely cool to see it in a syntax that's a little more familiar but the more I used it, when debugging for example, you don't have direct access to contacts and events and I don't know, it all seemed like maybe it's a little inconsistent with other projects that aren't doing it.
[00:09:30] So I just kind of felt like maybe the one of the upsides of using it didn't outweigh some of the more. Do you use it a lot? Did you find that it with the debugging that you can't actually directly inspect context, if you're like for debugger in the middle of a function that using the immer sign
>> So the question was about Xstate immer and being able to debug it and and inspect it and all that, and whether there were any problems with that. With immer in general, the way that immer works just real quick is that it creates a proxy from the object that you wants to quote unquote mutate, and that proxy is not really a good representation of the object, obviously, because there's a lot of getters and setters on there that are pretty opaque externally.
[00:10:25] So, what you could do is you could use the original function and console dot log that. So in immer, there's this extracting the original state from a draft. And you basically have this original function that you pass in the object where you want the original form of that object.
[00:10:46] And so you could console dot log that, and that's going to be the same as the original context.