In this lesson, we are going to learn how to abstract out state mutations into a reducer. In our current Angular application, all of our category state and business logic is handled within the category's model. We are going to refactor our code to entirely eliminate the need for our categories model by building out a couple reducers, and seeing how we can interact with them directly.
The one thing that I want us to notice as we build out our reducers is that a reducer itself is completely stateless because it receives current state every single time it needs to calculate and return new state. With this clearly defined contract of inputs and outputs, this makes testing reducers really easy, which is one of the huge benefits of using this approach.
To get started, let's create a categories.state file that's going to hold our initial reducer that we're going to build out. Let's start with a categories reducer. We'll give it an initial state of an empty array, and an initial category or action type of get categories.
We're going to just copy this, and we'll export this as a constant so that we can reference this action type in other places within the application. Let's just create a few comment blocks to separate our code, making it easier to read.
Once we have this in place, let's step through our reducer, and see what's going on. Within our categories reducer, it takes two parameters, as all reducers do. It takes the current state as the first parameter, and then an action object as the second parameter that has a type that we use a switch statement to then determine what is the next action we want to take, based on the action type.
In this case, with get categories, we're just going to return whatever we pass in. If we do not pass anything in, we just return state, just as we'll return state if there's an action type it doesn't recognize. Let's go ahead and use this.
By hopping into our categories.js file, we're going to import our categories reducer as well as our get categories action constant. Once we have these imported, we can hop down to our on init method, and remove this call to the categories model.
We're going to replace this with a call to the categories reducer. The first thing we're going to do to set this up is define an initial categories collection. We'll give it a single category of hello reducer. From here, we're going to set this.categories by calling the categories reducer, and passing in the state of initial categories.
We'll send in an action object with a type of get categories, without a payload.
Let's hop into the browser, and see what happens when this loads. You can see that we have this hello reducer category item as our only item within our categories collection that we're binded to.
Let's go ahead and abstract out this initial categories collection, and move it closer to the reducer. We'll just export this. Let's replace this single categories object here with a collection that is closer to what we're used to working with.
We'll set our initial to initial categories. From here, we can delete this, and just put in undefined. Let's go back to the browser, and you can see now that it is loading our initial categories, which is development, design, exercise, and humor.
Let's make a few more calls to our categories reducer. To make this a little easier to visualize, we're going to import the timeout service. We'll assign this to a local timeout property. From here, let's make a call to the timeout service.
We'll pass in the initial function, and we're going to define a payload object with a new collection of categories. We'll give the first one a name of redux, and then let's create another category item. We'll give it a name of angular.
From here, let's go ahead and update the categories collection by calling the categories reducer. We're going to pass in the current state of this.categories. We'll create an action type of get categories, and we're just going to pass in the payload object. We'll set this to fire in three seconds.
Back to the browser. Let's refresh the page. One, two, three, there we go. We've updated our categories collection by calling the categories reducer. Now you'll notice here that when we call categories, we're passing in a payload, which then it just returns the payload because we have defined it.
If we define an action type that it doesn't understand, it's just going to return state, basically performing a no operation, if you will. Let's just copy this block of code here, and we'll simulate what happens when we do that.
Let's create or update our payload collection to just have a single category of uh-oh. We'll create an action type that it's not going to recognize, which is get categories! We'll set this to fire at six seconds.
Refresh the page, one, two, three. One, two, wait for it, and nothing happens. Let's hop back into our code. Let's update the action type one more time. Back to the browser, and one, two, three. One, two, three, and there we have it.
What we want to do is replace this call to the get current category on the categories model with another reducer. We're going to create a reducer called category, give it an initial state of an empty object, and an initial type of get current category.
We'll copy this. We will define another action type constant. If we look at our reducer, it's very similar to the one above. It just returns the payload if it exists, or the initial state if it does not. Let's go ahead and import the category reducer, as well as our action constant.
We'll go down to, on category selected. Because it's a category reducer, we're going to update this parameter so it doesn't collide. We're now going to set this current category equals the call to the category reducer sending in this.currentcategory, which initially, it'll be undefined.
We'll give it an action type of get current category, and a payload of current category. Because we're setting this directly in the categories controller, we can just get rid of this call here, as well as this call.
We can get rid of the categories model entirely, so mission accomplished here. Let's comment this out, so it's a little easier to visualize as selecting the category. As we click through here, we can see that, within the component itself, we are keeping track of the current category.
Let's hop back into our code. We will uncomment this, and let's do a quick review of the reducers that we have created. We have our categories reducer, with a state of initial categories to start out with. We are passing in an action object that has a type and a payload property.
Based on the type, then we use that to determine what action we're going to take. We have the same thing with our category reducer. We've defined our action constants here, so we're not dealing directly with strings.
That allows us to import not only our reducers, but our action constants that we are then using to set this.categories within our categories controller. We're calling this.categories equals the result of whatever the categories reducer is.
The method for that every single time is we're passing in a state and an action item that may or may not have a payload object on it. We did the same thing with current category by calling the category reducer, and sending in the current category state, and a get current category action item, with a payload of current category.
This is how we use a reducer to manage state within an Angular application.