Check out a free preview of the full State Modeling in React with XState course:
The "Refactoring with useReducer" Lesson is part of the full, State Modeling in React with XState course featured in this preview video. Here's what you'd learn in this lesson:

David recommends refactoring any useState hooks to the useReducer hook as a first step for incorporating a state machine. The state logic is centralized within the reducer making it easier to understand and share across components.

Get Unlimited Access Now

Transcript from the "Refactoring with useReducer" Lesson

>> Okay, so how do we translate this to an actual state machine that we could use in our application? Well, before we get into x state, and this is something I recommend in general. It's good practice to try to refactor to use reducer instead. So Just going over how use reducer works.

[00:00:23] A user reducer is going to take two arguments, a reducer and an initial state. So let's define both of those right now. Our initial state is going to be pending. And then we're gonna call this an alarm reducer. And this alarm reducer is going to take two arguments, which is the States and the events that occurred.

[00:00:45] Now in the react docs you'll notice that the second argument is called the action. But I am calling it the events because it is a more accurate term. Action is sort of a holdover from the Redux days and since Redux is immensely popular. That's why you see action everywhere but actions means something else in state machines.

[00:01:05] So we are going to be using the term events. So this alarm reducer, it starts in the initial state, which is pending. Then we're gonna switch on what happens when we're in a state and an event occurs. Now if you're used to Redux or something similar, such as NGRX, you will intuitively do this with a switch statement, but it's a switch statement on the event.

[00:01:32] This time we gonna be doing things a little bit differently. We gonna be switching on the actual finite states, so we gonna switch on state. And we have three, yeah, three possibilities. We have pending. We have active and of course we have inactive. And we have a default, which default should never happen but, you know, just to be safe will return the state.

[00:02:03] Okay, so now that we're in the inactive state, that is when we decide what should happen when we're in that state in an event occurs. So. Let's just do an if statement. If event type is. We haven't defined it yet, but if that event type is toggle, then we gonna to return the next state which is pending.

[00:02:31] Now in the pending states, we gonna do, we also have to return the state. Otherwise if the event type is success, Then we go to the active states. Now in the active state. If the event is toggle, then we return to oops to be inactive state. Now we could use both that reducer and the initial state so I'm gonna put the alarm reducer over here in the initial state, and this gives us our status.

[00:03:08] And instead of set status, react calls this dispatch. So we're going to use dispatch for now. Now you're going to notice that things are going to be a lot less imperative. And that's because instead of directly setting the state We're gonna be dispatching events instead. So this timeout, we are gonna dispatch success, sorry, type success.

[00:03:33] And that's gonna be after two seconds. Let's take a look. All right. This is gonna be a lot simpler too, instead of doing all of this logic for whether the status is active, or inactive, or pending, we want to dispatch Type toggle. And then we could get rid of all this code.

[00:03:55] So while we did add a few lines of code, we removed a lot of the logic that was scattered around the component. And so this is extremely helpful to understanding the full logic of our component. So let's make sure now that this actually works. So we gonna be going back to our app and gonna click.

[00:04:18] And, so I guessed it did work, I was just impatient. All right, so it does work. So check the opacity as the status is pending. Okay, so if the event type is toggled goes to here, return, forgot to add my returns here. Don't forget to do that. All right, so now when we restart, we'll see that after two seconds, it does go, back.

[00:04:52] Let's see it's not toggling back. So let's check that real quick. All right, so on dispatch. Cuz I have it lowercase that. That would make sense. Okay. Third time's a charm, click after two seconds it goes there. Click again. Two seconds goes there, click again. And yeah, it works.

[00:05:21] So you might be asking yourself at this moment, why do I I wanna go through all of that trouble of writing an entire reducer when all I could do is juggle a few set states. Well, the reason like we started to see is that when you start to add logic, your components become more complex if you have what's called decentralized logic.

[00:05:42] Right now we're centralizing all of our logic inside this alarm reducer. So things become a lot simpler to understand since we could see at a glance what the status of the component are gonna be, which is inactive, pending or active. And we could also see what can happen in each state, so inactive we can only accept the toggle events in pending we can only accept success.

[00:06:06] And same for active we can only accept toggle. We know exactly which state it's gonna go to. So this alone is really helpful because that reducer is the centralized place where you could just see all the logic you could share it between other components. All of this becomes, you know, a lot more organized.

[00:06:28] Instead of Spread out. But there's also one more thing. Let's say that a toggle event happens while we're in the pending state. So essentially, you try to toggle the alarm on and while it's trying to save you say, you know what, I actually don't want that alarm. I don't want to wake up that early.

[00:06:46] So you press it again. Well, we could just add another, if statement here. If event type is toggle, then we're gonna return pending or sorry. Not pending, inactive. Now you probably thought that that that cancellation logic was gonna be a lot harder than that. But honestly, that's all you need, cuz instead of directly setting the state over here we're dispatching a type success.

[00:07:17] And remember, in the inactive state, we're not handling that success event. So we know that nothing is gonna happen there. So let's try out that logic and reload. Click this goes to click it. After two seconds it goes to active. Go back. So now if I click it, and I click it again, it goes back to pending.

[00:07:40] We know that the timer has been cancelled, but even if it hasn't been canceled, it's gonna send that success event. But that success events, we're gonna say hey, In the inactive state, we're not expecting a success event so we're just not gonna deal with it. And so, you know, this is a really great way to organize your logic