State Management in Pure React, v2 useReducer Introduction
This course has been updated! We now recommend you take the React Performance course.
Transcript from the "useReducer Introduction" Lesson
>> I kind of alluded in the last section that useState is great and it works. But it's also relatively simple, where you get a brand new value, and also a brand new function for setting that value to its next one to trigger out the rerenders. It's one of those things where most of the time, that's all you need.
[00:00:23] Some of the time, though, it's not what you need. Sometimes you need to do something a little bit more complicated, which again, we'll see as this course goes on. But also this idea that we get a brand new function for setting it every time is going to trigger a lot of rerenders.
[00:00:38] Because it's a brand new function which means we can necessarily mentalize it or anything along those lines. We're getting new stuff every single time. And so we have seen prior to hooks existing at all that there have been other ways of managing state other than just setting it, right?
[00:00:52] So Redux, for instance, gave us this idea of having a reducer that everything goes into and new stuff comes out, right? And that was kind of based on flux, which is another way to say things happened and you would modify some states. React these days has useReducer on top of useState, on top of useEffect, there is useReducer, which basically allows you to take a simplified version, what you might find in Redux and just have it available to you in React.
[00:01:21] Now, emphasis on the word simplified, right? Again, it's one of those 80/20 things, right? It does 80% of what Redux does. The other 20% can be a little bit difficult to implement, and we'll implement some of that as well. So there's a certain point of, does this mean I never have to use Redux again?
[00:01:41] I don't know, right, maybe, right? We still use Redux on purpose of tutorial for reasons that I will kind of talk about as we go through. But for simple cases, I don't know if I'd immediately pull in Redux as fast. So like most things, it depends. All right, so we're gonna look at the next app, which is this idea of grudge list, which is definitely not a to-do list at all.
[00:02:06] Cuz to-do lists are boring. This is just a list for keeping track of people who have wronged me. [COUGH] And you can see that there's no ability to remove anyone from the list. They can be forgiven, but they cannot be forgotten, right? So they stay on the list no matter what.
[00:02:27] You'll notice that this one does not persist to local storage. I'll leave that as an exercise for the reader. But they stay on the list and I can theoretically add a new grudge, I can forgive somebody, so on and so forth. And right now, let's take a quick tour of the code base.
[00:02:44] So we've got a few components. We've got index we just call as application. And we're using this useState in here. And we can see that we've got grudges and setGrudges. It's an array, arrays are tricky. React state needs immutable objects every time, right? When you set state, what React does is, is this different from last time, right?
[00:03:12] And if you're giving a primitive, like a number, it's like, well, yeah, one is different than two, no big deal, right? If you mutate an object, an array, they are the same reference in memory. So even though it has different keys or different values in it, React can't tell that it is a different set of values.
[00:03:46] So a lot of times, we can't just push something onto an array. We can't just add a key to an object or change a key on an object. We have to return a new object. We have to return a new array every time. So in the case of adding a grudge, we use a spread operator to say, hey, take everything that was in the grudge's array.
[00:04:04] Also add that new grudge onto the front of the array. But you can see we're using brackets. It's a brand new array that contains all of the contents of the old one, right? And those are copies. I mean, they are still the same ones in reference, so that's important.
[00:04:15] Sometimes we want the same objects in reference. If it didn't change, we don't wanna tell React something to change, right, cuz it didn't. Don't rerender cuz it is the same, and we put that new one on the beginning. Toggling forgiveness is a little bit trickier. We basically do this weird hack, where we will map through all of them.
[00:04:35] If it is not the one we're looking for, return the same object in memory. If it is, return a new one, right? Cuz this will tell that one that has to rerender. There are ways to get around this. There are libraries like Immer and Immutable.JS. The problems with those are, let's say the good things first.
[00:04:51] The good things about those is that you don't have to do all this kind of squarely stuff. You get to do stuff, the regular like, I wanna push something on, it will give you a new array. It does the right thing all the time with a simpler API.
[00:05:19] That'll depend on the size of your team. When I worked on a team of five people, yeah, totally. Now, in our code base, there are, I think, 27 front end engineers. That might be a slight hyperbole. I've lost count, but a lot, right? And so then the education piece and the onboarding piece becomes trickier.
[00:05:38] So those decisions sometimes are not necessarily technical, but they are also just related to the dynamics of your team. So yeah, here we're mapping and we're doing something like that as well. We're passing in addGrudge. We're passing the grudges. And we have to do this little thing that we'll talk about in a second called prop drilling, which is we've got grudges, plural.
[00:06:00] Grudges plural goes ahead and then renders individual grudges based on that array, a little bit of a heading. As you can see, it needs to get onForgive, because it needs to pass it to the individual grudge. It doesn't do anything with itself. It simply takes it from the parent and hands it to the grandchild.
[00:06:20] This is fine in a small application, right? But in the Redux cuz we have one with a big combo on board, I'll show you a chart of it in a second, right? It gets really complicated cuz you have deeply nested trees. The highest common ancestor might be very far up there, and refactoring becomes hard.
[00:06:38] So we're gonna deal with some different things at the same time. We're using this application cuz it's somewhat simple, but it does begin to show us how we can implement a very simple useReduce or pattern. And we'll talk about how it can get more complicated. The other thing is things are gonna rerender because, as I mentioned earlier, we're making brand new functions and arrays every time, which is React is learning, everything changed.
[00:07:02] So we've got grudges plural. We've got the grudge singular, right, which is just taking that kind of generic onForgive and calling it with the right ID, so we know which one to map over. We've also got NewGrudge, which has its own state, right? It's keeping track of those input fields.
[00:07:21] Now, if I move that up to application and pass it in with the handlers, that would work, but it would actually trigger application to rerender every time, which would trigger all the grudges plural and the grudge singular. So by putting it in the lowest component on the tree that needs it, my life is a little bit easier.
[00:07:38] We'll talk about that again in a second. And then it has this handleChange event, which is when they submit, okay, now, that's when we wanna call the addGrudge. So we can see an application, we pass it, addGrudge, and it can call that and add it to the array.
[00:07:53] So one of the things that you'll notice, so by going to NewGrudge, we'll just do this in console.log. We'll say, rendering new grudge, and let's do that also in grudge singular. Cool.