This course has been updated! We now recommend you take the Intermediate React, v5 course.
Transcript from the "Reducers" Lesson
[00:00:00]
>> So now we have the store object. This is what actually all of our data is going to be stored. It's aptly named, I'll put it that way. Okay, so now I want you to make a folder inside of source and I want you to call it reducers.
[00:00:24] Okay, instead of reducers, I want you to make a file called index.js. And I want you to import something called combineReducers from Redux and then we're going to import location, which is something we're about to write from dot ./location. Okay, and then we're going to export default. CombineReducers, which is gonna be an object and we will wanna put location, location.
[00:01:03] So location is going to be like a sub reducer that we're about to write. And if you're familiar with JavaScript, you know that if you have something, the key and the value are called the same thing, you can just do that, right? So location, Those two lines are equivalent, right, line five and line six.
[00:01:25] So because this is really nice to read, we're just gonna do that. So let's talk about what combineReducers does. combineReducers is a kind of just a little helper function that Redux provides for you. Is like, all right, for all of my various different pieces of state, I'm gonna have one little tiny reducer that handles it, right?
[00:01:48] combinedReducers says, all right, what I'm going to do with this location reducer is I'm gonna have a key called location. I'm going to call this micro reducer that's going to just handle the location one. And then I'll make like an overarching parent reducer for you. You can write your own parent reducer, it would just look something like this, function reducer.
[00:02:11] It would take an action and a state, you don't have to follow along by the way. And then it would just be a giant switch statement. Switch, right, and you'd switch on (action, type). And then you would say case, location, or change location or something like that, right?
[00:02:32] And then you'd just write out this giant switch statement. Actually, if you remember from the useReducer section, we actually wrote one that looks exactly like that. So this reducer right here, this is exactly what a parent reducer would look like. In this case, do this. In this casem, do this.
[00:03:01] And instead of saying return here, what you would be doing is, all right, in case INCREMENT_R called the R reducer, called the G reducer, called the B reducer, right? This gets really burdensome to write over and over and over again and kinda hard to read. So combineReducer makes it really easy to just say like, all right, I have a location reducer that only thinks about the location.
[00:03:23] So set that to be the case for this one. Up to you. Actually, in previous versions of this class, I actually taught it, where we actually coded this out and we did not use combineReducer. But at the end of the day, whenever I do write Redux, which honestly is not frequently anymore, I always use combineReducer cuz it's so helpful.
[00:03:46] Okay, one more thing I wanted to call out here, store. I noticed insanely important reducer from ./reducers and not ./reducers/index. This is just like a little node trick that you can do. If you say import something from a directory and you don't specify which file, it'll automatically look for an index.js.
[00:04:09] So in this particular case, I'm not specifying to import from index.js, but it is, because that's something that no JS just does automatically. Okay, so let's go make the location reducer. When we're at location.js, this is going in the Reducers folder. And here, we're going to make a very small little reducer.
[00:04:36] Export, default, function, location, And you have to give it a default value. So the way to do that in JavaScript is to say a state. If here's my default state, which I'm gonna call "Seattle, WA" for my default state for the location, but you can put whatever you want.
[00:05:01] And then the action, you do not wanna default for the action. And then you're gonna say switch (action.type) in the case that you were get issued a "CHANGE_LOCATION", action type, then return action.payload, otherwise, return the state. A lot of reducers end up looking pretty similar to something like this.
[00:05:38] So let's talk about something like how the reducer has to work. Every reducer without exception will take in the current state and then we'll take an action, that action must have a type. The only thing that it must have is a type. Okay, so in this particular case, the only action that this reducer cares about is a "CHANGE_LOCATION" action, in which case, you just return whatever that action payload is.
[00:06:11] So this could come in with like Salt Lake City, Utah, right? And it will return Salt Lake City, Utah, and that would be the new state, right? We're not doing anything special here. I don't know if it was important that it was always to uppercase. You could totally do something like to upper case.
[00:06:28] In which case, you could give this any sort of action payload here and it will always return to something uppercase, right? But any sort of modification you need to do, you would do here. Okay, if any other type of action gets called with this, then you just wanna say like, don't change my location, right?
[00:06:49] If this gets called with change animal, that doesn't affect the location, so it's just gonna do that. So something key to note, this reducer will get called with every single action, whether or not, this reducer cares about it. This reducer will be called with every single action, which is why we have to have this default case here.
[00:07:13] So if I issue a change animal action, this reducer is still going to run. Which means if nothing changed, then just return the state as it was. If you return the same state, then you're telling Redux nothing changed here, therefore, do not update anything and subscribe to my changes, right?
[00:07:32] So for example, if you have this SearchParams thing, all it cares about is location. And location doesn't update but something else upstate. Redux won't inform it as long as location itself didn't change. And we'll talk more about that once we actually start plugging everything together, Okay, so that's a reducer, yeah, just a couple of key things to keep in mind here.
[00:08:04] It has to return the state always. It has to have a default state and it has to accept actions. Notice I called this action.payload, that's not required. Though, I'll say it's common. Like whatever things that you're updating with, there's a thing called a flux-standard-action, flux-standard-action, I think is what it's called.
[00:08:38] It's a spec that someone wrote that basically says like, hey, there's a lot of Redux like libraries out there. Let's all make all of the action shapes the same thing so that if we have action type libraries, they can interrupt with each other. I'll say that I don't know of any libraries that actually depend on this.
[00:08:55] So the value prop is nebulous. I just liked that someone else thought about this and came up with a good idea so I don't have to think about it. But okay, everything has a type that's 100% true. They'll have a payload if they have something to dispatch to that particular reducer and then they also can have an error, so, Must have a type, can have an error, can have a payload, and can have a meta property.
[00:09:25] So meta can be stuff that's like extra information. Maybe it might be like telemetry. It might be timing, it might be something that doesn't have anything to do with the payload, but it's just additional meta information about the action. Actually I'll say that I've used error before, I've never used meta.
[00:09:44] Okay, so that's why I'm calling things the way that I'm calling them. This is not required, it's just I think it's a good idea maybe, okay? So this is now a incredibly testable reducer, right? This has no side effects, which reduces can't and they shouldn't, or anything like that.
[00:10:10] All it does is like, given a state, given an action, do I get the correct new state out of it? So if I call this with Seattle or if I call the state, the old state Seattle, and I give it with an action.payload of Salt Lake City, and I call that ten trillion times.
[00:10:28] On the ten trillion first time, what is the new payload? Salt Lake City, right, cuz it's just overriding it, right? So old state = Seattle. And then action is type, CHANGE_LOCATION, and payload. Salt Lake City or something like that, right? The new state at the end of this would end up being SLC, right?
[00:11:10] This is extremely nice because now I write one unit test and I guarantee you this never drifts, right, that I will not have bugs here. This is why use Redux, is this particular part is this, this delta in old state and new state and making that very testable.
[00:11:26] That is why Redux is great. Okay, I hope I've belabor that point enough. [LAUGH] So let's go make the rest of the reducers that we need to make now. We're gonna make another one called theme.js and you could probably just basically copy and paste this one. And the default is gonna be darkblue.
[00:11:57] This is gonna be called theme, and instead of change location, it's gonna be CHANGE_THEME, and that's it. There's probably some library that will generate these really basic ones for you. I don't know what it is though. Okay, we're gonna do animal as well, oops. Animal.js, this will be called animal.
[00:12:35] The default animal is no animal, and this only listens for animal changes. Okay, one more. We're gonna make breed.js. This one actually has something more, which makes it a little bit more exciting. I guess it depends if you get excited about Redux, in which case I have questions.
[00:13:02] Okay, this is gonna be breed, the default breed is no breed. This one listens for obviously change breed themes, so CHANGE_BREED. But this will actually listen for one more action type. And this will listen actually as well for a case "CHANGE_ANIMAL" as well. If you remember, we don't wanna have poodle cats or cockatiel dogs.
[00:13:31] So we'll say return empty string in that particular case, right? So if anyone changes the animal, we wanna set breed to be empty string. So this is why it's actually advantageous that every reducer gets called with every action, cuz now, multiple reducers can concern themselves with one action.
[00:13:57] Okay, now all we have to do is go back to our index.js and import all of these, import breed from our /breed. And we'll do animal from animal and we'll do theme from theme. And then again, we'll do breed animal and theme. Okay, and now all of our reducers are setup.
[00:14:33]
>> So just to confirm, so our combineReducers, that's just making sure that these other individual reducers we set up all run at the same time, right?
>> This combineReducer is generating a function that basically does this. And it's just like a much longer one that we don't have to care about.
[00:14:54] [COUGH] The other nicety that it does for you is, notice here, we're not listening for the entire state object all at once. It's actually giving you just the state of animal. That's the other nicety that it does. Otherwise, instead of saying return state here, you'd have to do like an object of a sign to make sure that you're modifying just that part of the state.
[00:15:17] combineReducers takes care of a lot of that just plumbing up for you.