This course has been updated! We now recommend you take the Intermediate React, v5 course.
Transcript from the "Creating Reducers" Lesson
[00:00:00]
>> One of the things that's kind of interesting that node will do is if there's a directory called reducer. So let's go ahead and create a directory inside of src called reducer. And inside of reducer, we have a file called index.js, like this. Now we could say /index like this, and that's probably even better and more correct to do it this way.
[00:00:25] But if you have a file called index inside of a folder called reducer, it'll actually automatically look for a file called index.js. Just a fun noteism. Let's just be explicit and we'll put index there. But both of those will work, you'll see that in my notes. Okay, here we're gonna import a function called combineReducers.
[00:00:53] Sorry, this is the index.js inside of reducer. And again make sure that the reducer folder is inside the src directory. { CombineReducers } from 'redux'. And we're gonna import something called location, which is gonna be another reducer from ./location, we still have to go code that up, okay?
[00:01:20] Here we're going to export a default combineReducers. And this is gonna be location:location. Or if you aren't familiar with recent JavaScript, if it's called the same thing, if the key and the value are the same thing, you could just do this. That's the same thing as location:location. It's just a shorthand way of doing that.
[00:01:47] So what is combineReducers? This auto generates your rootReducer for you. So it knows that there's going to be a reducer here called location. And it's going to deal with the location part of your Redux store, right? Cuz again, you can think of a Redux store like this. So I'm gonna have a location thing and that's gonna be like Seattle, WA.
[00:02:18] And then maybe I have an animal portion. And that's gonna be dog, right? So a Redux store in reality is just a giant object. And so by putting something here called location and I'm saying that this particular reducer handles the location part of my state tree. So whenever this location reducer that we haven't written yet that we're about to write gets called, it's going to automatically be passed the location from the store.
[00:02:46] It's only gonna worry about that portion. Does that make sense? Let's keep going. Hopefully, we'll make sense as we go along. So make a new file here. And we're gonna call it location.js inside of the reducer folder. In this, we're actually going to write our first reducer here.
[00:03:10] So it's gonna be export default function. You can call it location. You can call it location reducer, whatever you wanna call it. I have it called just location. The reason why you wanna give it a name is if it hits a bug, you'll see it in your stack trace called whatever you call it here.
[00:03:29] So it just helps us with debug ability. Okay, it's gonna take in state and action. And then you're gonna say switch, Based on the action type. And then you're gonna say case if it's a CHANGE_LOCATION action, then return action.payload. Default, if nothing changes, then return state. Okay, so let's talk about this for a second.
[00:04:13] One, I used a switch statement, I could have used an if statement. Totally could have done that. It's just kind of reducers tend to use switch statements a lot. So this location one only has one type of action that it responds to. But it could have 15 types of actions that it responds to.
[00:04:29] So here in the future, now, if I have, let's say a case UPPERCASE_LOCATION, then I can say, return state.toUppercase, right? And it could respond to both of those, right? So we're kind of setting ourselves up to expand upon these reducers later. So let's talk about the action. Every action always has an action type.
[00:05:02] 100% of the time, every action will have a type. That's the only requirement, they must have a type. It's always gonna be something in uppercase usually. At least you should always make them that way, that's just the way you do it. They frequently will have things called payloads, which is kinda like the parameters that this is being called with.
[00:05:27] So let's say this one here for me is gonna be Salt Lake City, UT. So that would be what I would want to change from Seattle. Let's say it started out Seattle and it came in with a payload of Salt Lake City, then I would change the state of the location to be Salt Lake City.
[00:05:46] So that's the kind of things you'll put in payload. And this can definitely be, payload could definitely be a object as well. It could be city, blah, state, blah. And then I could reduce that down to just like a Salt Lake City, UT, right? It's up to you.
[00:06:04] It depends on how your application works. In this particular case, we're taking a pretty simple case which is gonna be payload Salt Lake City, UT. They also have some other things. Sometimes they'll have error, they'll have meta, there's a bunch of things. There's a flux standard action of how, and I actually have that linked there in the notes if you wanna go check that out.
[00:06:24] But that's generally how these things are structured. There's nothing about Redux that's going to enforce that for you. But it's just kind of the methodology of how one writes Redux. Okay, so we have the change location. If someone's trying to change it from Seattle to Salt Lake City, we'll return the action.payload.
[00:06:46] That'll go back to the rootReducer, the rootReducer will update from Seattle to Salt Lake, right? What about this default return state? If nothing changes, you still have to return the state, right? So let's say another action comes in and it's change animal, right? This rootReducer will actually still will be called.
[00:07:06] So you have to say, okay, this is not something that I'm gonna pay attention to. So I'm just going to return what was there previously, right? So change animal to dog. The location reducer will just say, okay, cool. Seattle didn't change. I'm going to return Seattle again. So that's a big key with Redux is that if you're not doing anything, just return what was there.
[00:07:31] Last thing that we need to do here, we need a default state. So by default, what should this be? It could be empty string, right? So if you wanna have no default location, if you wanna have the default location, be Seattle, Washington, let's do that, right? So now, the way that Redux initiates itself is it calls with a blank action.
[00:07:58] So it forces every reducer to run once to get its default state. So in this particular case, the default state of this reducer is gonna be Seattle, Washington. So on the very first render, this will be Seattle. And this is just a default parameter here. That's a lot of talking.
[00:08:15] But you understand one reducer, you understand every reducer, right? Every reducer is going to look exactly like this. There is no variation here. The other thing that I'll throw out here is TypeScript and Redux are actually a joy together, because they will enforce all of the object shapes for you, which is really nice.
[00:08:42] Reducers are synchronous, always. Redux has the ability to have asynchronous middleware. So you can have lie things like thunks or sagas or generators or promises. There's a bunch of different flavors of them. Not gonna teach them today. But basically what those are gonna do is going to force Redux to kick off a side effect, which will then eventually return something that will go into the reducers.
[00:09:06] But reducers themselves 100% of the time are always synchronous. They also have to be pure with no side effects. Notice that if I call this reducer 10,000 times with the state of Seattle, Washington, on the 10,0001st time that I call this, what is going to be the result of state?
[00:09:31] Seattle Washington, right? So that means that it doesn't matter how many times I call, as long as the state and the action are the same, it's always gonna be the same result. That must be true as well for reducers. Cool, so now we have a location reducer. Now we need to make it so somehow we need to get an action into here, right?
[00:09:57] So let's go ahead and go think about that. Or actually, you know what, while we're here, let's go ahead and make all the other reducers. So I'm going to make a new file. And actually, you can just copy all this cuz they're basically all the same, new file.
[00:10:20] This is gonna be animal.js This is gonna be animal. This is gonna be change animal. And the state by default, we can make it whatever you want. Let's just make it dog for fun. That's basically it. Now you might be asking, this is a lot of repeat code.
[00:10:49] Is there something that can help me with this? And there are libraries that will actually generate some of these reducers for you. But we don't have that many, let's go ahead and code them by hand. We got one more or two more that we're gonna do. We'll have one for theme.
[00:11:04] Theme.js. So this is gonna be theme. This is gonna be dark blue by default. And this is gonna be CHANGE_THEME. One more, New File. This one's gonna be breed.js. Same thing, breed, and this can be empty string if you want. Or we can make it Havanese, whatever, up to you, whatever you want your default state to be.
[00:11:40] And this will be change breed. Okay, now we're gonna go into our index.js. We're gonna import breed from './breed'. And let's just do two more, animal and theme, okay? So now those are all of our reducers, breed, animal, theme. And there we go. Now we have a rootReducer that can handle location, breed, animal and theme in the various different parts of our application.