Intermediate React, v3 useReducer
This course has been updated! We now recommend you take the Intermediate React, v5 course.
Transcript from the "useReducer" Lesson
>> Some of you are perking up, it's like reducers? This sounds like Redux, right? And that's because it actually is very, very similar to Redux. And there are gonna be people out there that tell you, I never use useState, I only ever use useReducer. So it's basically a thin layer on top of useState that allows you to treat your state like a Redux store kind of locally.
[00:00:23] So let's just have quick example of that. I have this useReducer h1, and if I click plus 1 on the red, it goes red. Or if I do green, it goes green, or I can add blue, and I can turn it white if I add of them, right?
[00:00:40] So it's just allowing you to change the color values of the various RGB spectrums. So let's take a look at how we end up implementing that here using useReducer. I'm actually a fan, I think it's pretty great. And in specific cases, I will pull on useReducer instead of useState.
[00:00:56] But just kinda keep that in mind. I'm either gonna use useState or useReducer, they fundamentally achieve the same things in relatively different ways. Okay, so I create a reducer here. And if you've ever written Redux before, and if you haven't, we're about to, so don't worry about it.
[00:01:15] But this functionally works like a reducer in Redux. It gives you the current state and an action object. Okay, and then you just switch on the action object. And here I have a couple of different types of actions, increment red, decrement red, increment green, decrement green, increment blue, decrement blue.
[00:01:37] And all I'm doing here is I am changing the state so that the red either goes up or the red goes down, or the green goes up or the green goes down, right? And then all I'm doing is I'm modifying my state in some capacity, and then I'm returning it back.
[00:01:54] So you can see here, I have useReducer. I hand it this reducer up here, right? And a reducer's just a funny way of saying a function that takes in a state, it takes an action, and then it returns back the state at the end. That's literally everything that a reducer is right there in a nutshell.
[00:02:13] It's a function that has a specific way of calling in a specific thing that returns. Okay, so you have a useReducer here, I hand it the reducer function, I hand it an initial state. And then here I get back the current state and a dispatch function. And a dispatch function is, you hand it an action item and then it calls the reducer with the current state and that action item, that's it.
[00:02:43] So down here, I wonder what this is? Yeah, inaccessible, that's why there's a warning here. And then any time that I click the click, it dispatches a action item here of either increment red or decrement red, depending on which button they pressed. And the same thing here with increment green or decrement green, increment blue or decrement blue.
[00:03:10] And it just goes through the reducer, It modifies the state in some capacity. In this particular case, red will go up by 50 or down by 50. And then yeah, the limitRGB function is just keeping it between 0 and 255, which are the valid values for RGB. So the question that would be valid to ask at this point is, why would I choose this over useState?
[00:03:36] This looks a little bit more complicated, and I mean, perhaps it is. I mean, useState, you just say, here's my default state, give me back a different piece of state. I could've totally done this with const [r, setR] = useState(50) or something like that, right? And then done that for red, done it for green, done it for blue.
[00:04:02] And then instead of dispatching an action here, I just would've called setR or setG or setB, right? Totally valid, would work exactly the same way. When people just like this methodology of using reducers, because they've used Redux for so long they wanna keep using that. That's one reason that people use it.
[00:04:24] But the reason that I think that this is, perhaps in this particular case, useful is you have a lot of state that's modified in relatively uniform fashions, right? So this is either going up or down by 50, right? So this actually makes this pretty easy to understand. It's like okay, we can go up or down by 50.
[00:04:42] And you can kinda see at a glance here, this is all of the way that my state changes in a single glance. And let me just tell you that the source of almost every bug is just state changing in the wrong way, right? And so in this particular view, I can see at a glance all of the delta, right, all of the change that this particular component can go through and at a single glance.
[00:05:06] And I can kinda comprehend it as a whole. That's valuable, right? That makes it very readable. The other thing that I'll say is, I can pull this reducer out now into a unit test. And I can unit test that if I give it this state and this action, that it comes back with the state that I expect.
[00:05:24] Which means that you get to change or you get to test how your state will change, and that is very valuable. So then you can write unit tests around that and prevent bugs from state changing incorrectly. Which, like I said, is the source of most bugs. And if you can test the source of most bugs, you can have more confidence in your application.
[00:05:43] That makes sense? You're kind of decoupling state change from the display layer, and that's useful. So again, some people say, I always do this, and I only do it this way. And I would say that can be valid, right? If you're into that, you can definitely go down that path.
[00:06:00] Do I do it? No, no, definitely not. Cuz I'm lazy and I just wanna write useState, right? I lean into the convenience probably a little too much. But for something like this component that I made here, I'm pretty happy with using useReducer here. And I'm totally fine if you say, I'm gonna do it sometimes, I'm not gonna do it sometimes.
[00:06:18] I think that's a pragmatic approach to do it that way. Should I ever use Redux and useReducer at the same time? And I would say I think there's occasion that you would use both. Because you're gonna use useReducer as a local piece of state, right? So in this particular case, I probably don't wanna store this RGB in a global store.
[00:06:38] And I think the proper way to use Redux is, it's for global state only. I don't keep local view state in my Redux store. I use state for that, cuz React is really good at managing state. So in that particular case, I would use useReducer for the local view state and I would use Redux for the global state, right?
[00:06:58] And now, kind of a superpower they've got kind of accidentally in this process is, you can now use reducers both in your global Redux store and in your useReducer. I don't know if that would ever be useful or if you'd ever have occasion to do that. But now it's possible and you're using one programming model for your entire application.
[00:07:16] I think that's valid as well. So yeah, it's definitely possible and perhaps even a good thing to do. I haven't done it, so I'm not gonna tell you that I've done it. But it sounds like a good idea to me. Could you use useContext together with useReducer and basically kind of recreate Redux?
[00:07:34] Seems plausible, seems like you could definitely do that. The other thing I'll say about, at that point, if you're really trying to recreate Redux using useReducer with context, just use Redux, right? Redux has other things on top of it. It has additional tooling, like the Redux dev tools, it has middleware.
[00:07:53] So if you need to do Redux sagas, or thunks, or things like that, stuff that we're gonna talk about in that section. But if you're gonna do that together, just go ahead and go full in and use Redux. You also have the Redux hooks, which are really convenient to use as well.
[00:08:13] So right now this is black, right, this useReducer. So that means its color here is 0, 0, 0, right? I'm gonna click Increment red, so the plus on the red one, so this one here. So it's gonna call this onClick handler, which is gonna call a function that's gonna call dispatch with this action type in it.
[00:08:35] This action type, all it has is a type on it that says increment r, right? So I call dispatch with this object, plus. You probably can't see it, this got a little bit more red. Let's click it three times. All right, so now this is like 200, okay?
[00:08:53] So when I click this again, it's going to dispatch this object. Which is then going to call this reducer with the state, right? So let's pretend it came in with 0, 0, 0, this is the state object right here. So r 0, just type it out. R 0, g 0, b 0, that's gonna be the.
[00:09:27] What did I do here? Okay, that's gonna be the state right there, the r 0, g 0, b 0. And then the action here is going to be just an object that has type INCREMENT_R. Okay, so that's gonna be the action that's gonna come in here. So then it's gonna go to the switch statement, it's gonna say, what is the action type here?
[00:09:55] It's gonna be type INCREMENT_R, so it's gonna hit this first case right here, right? And then it's gonna return an Object.assign, empty object. One big key here is, make sure you're always returning a brand new object, right? So don't return the old object, or the reducer thinks nothing changed, right?
[00:10:11] Cuz it's not doing a deep check, it's just doing a simple shallow check, same object, same object? Cool, I'm not gonna update, same object, different object, I'm gonna update. That's the simple algorithm it goes through to, if the useReducer is going to update the view. So I assigned the old state, right?
[00:10:30] So g and b are coming across, they're gonna stay exactly 0, we're not overriding that. And then here, we're going to add 50 to the red. So this is gonna come back from r 0, it'll come back as r 50, right? And we'll return that, which is the new value of the store.
[00:10:50] It's going to call rerender again here, which is going to make it more red. So that's step by step what happens. And again, if you've ever done Redux, that should feel very familiar, that's exactly how Redux works.