Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
  • Overview
  • Transcript

2.2 Reducers and Actions

The reducer function is the core of every Redux application—it tells our data store how to mutate data. In this lesson, we’ll create a reducer.

Related Links

2.2 Reducers and Actions

When you're learning about React, you usually hear a lot about state.State is simply a set of values that describe the application, as the user sees it right now. This could include both data that the application is using, in our case that will be our flash cards and our decks of cards and it could also include a UI State, like which a deck is currently selected or if the user is in study mode or not. Reacts components are usually rendered based on a given state. Now, if you're just using React by itself, you can manage your state however you want because remember, React itself is just a view library. It doesn't actually manage the other parts of your application. But we're going to be using Redux in this course and Redux gives us an architecture to manage our state with, and this architecture is called, Stores. Now of course state is something that changes often, right? Because every time the user adds a card state changes every time they click the new card button and where showing a new modal, that's potentially a state change because we're changing the view that the user is seeing. So the state is changing all the time. And Redux stores are how we can manage these state changes. Redux uses a combination of actions and reducers to manage these state changes. So let's look at a basic example of this. We're here in our app.js file and from our previous lesson, we just have this single console.logline So let's get rid of that. and let's create a store. So all say const store equals and we'll say redux don't create store. Of course this is a function that Redux gives us and we have the Redux library of course remember because we pulled it in through the cdn, we have to pass a function to the createStore method, and this function takes two parameters. The first one is the previous state, and the second one is an action. Now, remember that Redux stores manage state changes through two things, actions and reducers. So obviously the action perimeter here is the action, the reducer is the function that we're writing here, and the way a reducer works is it takes whatever the previous state was, it takes whatever action is happening right now that is changing the state, and inside this function we will make a change to the state based on that action. Now, how do we know what to do based on the action. Well we have action.type. Having an action object with a specific type has been a pattern influx, which Redux is based on and Redux stores actually expect our action objects to have a type property. So what we can do Is we can have a switch statement here. And depending on what that action type value is, we can perform some different action. All right, for example let's say the case is that action type is add_card, right. This could be an action that takes place whenever we're adding a new card. To our deck but what do we wanna do in this case? Well let's create a new card. So we'll say let new card and we'll use object.assign and one of the big ideas in Redux is the idea and one of the big principles and Redux is the idea of immutability, which means that we never actually change an object or an array or any kind of value, instead we create a new value and this way. We can be sure that we're never accidentally breaking something because of changes. Immutability is one of the main principles of functional programming. And it's where we get things like pure functions and things like that. And so we're going to be building an app here that works with the principle of immutability that is we're never actually going to change an object but always be creating new objects. However, I'm not going to go into too much depth on why this is a good idea if you're interested in this. I'll have some links underneath this video, where you can check out some more resources on immutability. And the reason I bring this up now, is because object.assigned is a native JavaScript function that we can use to create a new object based on another one. We've all used libraries like underscore or low dash and you may know that they have an extend function which allows you to basically copy the properties from one object to another. And so we're going to do here is we're gonna say object.assign. We start with an empty object. Whatever we have as the first parameter to object on a sign is actually the object that will be returned, and so we're just going to start with a fresh empty object then we use action.data. Now, our action object here can have both a type and then some data to go along with the action. So when we're adding a card, the action is of the type ADD CARD, but then there's the actual data of the card that we want to add, along with that action, and we can get that through action.data. I should mention, will be creating these actions in just a couple of minutes here, so you'll understand how this all works. We're starting with an empty object. Then we add all the properties of action data onto this object. And then let's add a few more properties. We'll start with a score of one. For every card, we'll begin with score of one. Let's give it an I D, and for now we'll just be using the date. And by putting the unary plus operator here, we just convert this new date object into a number. Which is of course the number of milliseconds. From the Unix epoch which means, that this will always be a unique number. It's kind of a lazy way to do ideas but it's enough for this system. Okay, so now that we have this new card, we need to actually return the updated state for application so return a new object and once again will do object dot assign. We'll create a blank object, then we'll copy all the current properties of state onto that new object. So if we were just to end this object out assign call here. What we would return is just a new object that looks identical to the old state object. However, we need to modify that state object. So let's overwrite the cards property on this state object and let's replace it with state.cards.concat and I happen to know that state our cards will be an array. We'll see where we set the default to just a second here. And when you call concat on array, you actually return a new array. So this way we're not mutating, whatever our old state.card's value is state.cards.concat and of course we have to concat a new array onto our original array and this array will just have the new card in it. And so now, we have a reducer that can manage one action type. To review, we have some given state at some point in our application's life cycle. An action occurs and this reducer is called. If that action type is add card, then we'll create our new card object and then we will return the new state with that card being put into our new state.cards array. All right. Now obviously we're gonna have a lot of actions that are not ADD_CARD. So it's important that our switch statement have a default. What do we wanna do if we receive an action that we haven't planned for? Well that should be pretty simple. We will just return the current state. Now when Redux creates our store for the very first time, when the page is first loaded, state is going to be undefined. So what we'll say is we want to return to state or if that's a false value, let's return an empty object and we'll also have cards as an empty array there. Now this is one way we could do it by putting cards as an empty array here, and we would have to do this based on the code that we've written for add card because we expect state.cards to be an array. An alternative here would be to just return an empty object here, and if we did that well then state.cards.concat would break because state.cards is undefined. So what we could do here make this a little bit more robust is say state.cards and if that exists in our turner expression. We will do the con.cat otherwise, we will send cards to just be an array with just a new card in it. Now, obviously this store is going to be continually changing and eventually, we're going to have an application that will update whenever the store changes. So how can we watch this store for changes. It's actually pretty simple, we can just say store.subscribe and this will take some kind of function and inside this function we can do whatever we want. Now notice, that we don't actually get the new state here as a parameter to subscribe, instead to get that we have to say store.getState. So why don't we just console.log the current state for now, whenever the state changes. Now, remember we still have watchify rebuilding our code, whenever we save it and all those changes have been noticed by the live server. However, if we come to the browser, we haven't actually made any changes to our store yet, so we won't see anything in the console just yet. However, if we send our store an action, we will see some changes. So we can send our store in action by doing store.dispatch. And all we have to do is send it an action object. So we can set the type equal to ADD CARD and we can set the data equal to whatever we want. So our card might have a front and for now let's just say front. And we'll also have a value for the back of the card and for now we'll just say back. Now notice how these type and data properties are the same things that we are expecting inside of our reducer. The type is important and this is something every action is going to have, a lot of our actions will have data but as you'll see some of them won't. So now if we go back to the browser, you can see that we have an object here being logged to the console and this is of course coming from our subscribed call here, we subscribe to the store and whenever there are any changes we get the current state and we log it out. So, you can see if I open this up. We have one card here, with an object that has the front front back back and we also have our ID and our score. Let's give this a try again. We can dispatch another actions, or say store.dispatch type equals ADD CARD, data will just be an empty object for now. And now, if we come back to the browser you can see that we have two lines showing up here because console.log is called twice. Because the first time is for the first change to our state, and the second time is for the second change to our state. So this is our first lesson on using Redux. Now as you can see at this point we haven't even talked about react in the context of using it with Redux. And that's because what's important at the beginning, I think is to understand exactly what Redux brings to the table. Redux has nothing to do with viewing or displaying the data. What Redux is really all about is managing your state. What we've just seen here is really the core functionality of Redux. In fact I think it's fair to say that after this lesson you probably understand 75% of what Redux does. Its main purpose is managing your state through actions and reducers. Of course, there are a lot of other tricks that we'll learn along the way but this is the very core of Redux's functionality.

Back to the top