[2020] Redux Tutorial with Hooks and Thunk 🦉
What Is Redux?
Redux is a state management library that allows users to maintain the state of the entire application in a single immutable tree. If you have worked with React long enough, then you know that there is some pain in dealing with handling the states when your application scales up. Moving one state from one component to the next and drilling it down further causes a lot of frustration and annoyance and to be quite frank not a really good solution in handling that… Redux is able to solve all that.
Redux helps you write applications that behave consistently, run in different environments, and are easy to test.
Concepts of Redux
The frustration that comes along with learning Redux is that it is very hard to get started. There are too many components that you have to set up before you are able to use it. I’m here to tell you that, it is simple. To understand Redux, we need to fully grasp the fundamental ideas behind it. For me, I like to think of Redux as a tool that is made up of three main ideas: store, actions, and reducers.
Store
Think of the store as a home for your entire application’s state. It is simply a plain JavaScript object that contains key-value pairs for any state that you would like to store.
Actions
Actions are plain JavaScript objects that describe the type of interaction that you would like to see happen. For example, SET_INCREMENT
could be a type of action that increases the counter value. Or FETCH_USER_DATA
could be another type of action. Again, it is a JavaScript object that describes what you would like to do.
OR
Above is usually the convention of what an action function would return. Type is the type of event that you would like to see occur, and payload is the data that you would like to pass to make change the state that resides in the store.
Reducers
Lastly is reducer. Reducer is a function that essentially facilitates which action you would like to see occur and how to change the state based upon the action type. Think of it as a bunch of if-else statements depending on which action gets triggered will change the state-based upon that.
Let’s Get to an Example
The best way to learn is through examples, so let’s create a small sample application that utilizes Redux.
The application that we are going to be building is a number counter which displays the number and two buttons that we can press to increment or decrement. The first thing to do in this scenario is to create a Redux store.
There are a lot of things that are going on here, so let’s unpack it. We import a createStore function from the redux library. The createStore function takes in a reducer (we’ll talk more about how to create the reducer later). Provider is a Higher Order Component (HOC) that react-redux provides that will wrap our Redux store in. The Provider will allow us to access the state inside of the store from anywhere inside of our application because it is the root component. There are still some things that we will need to do in order to access it, but we will get to that.
The next step is to create the actions. The two actions that we are going to create are SET_INCREMENT
and SET_DECREMENT
.
As mentioned in the previous sections, actions are just a plain JavaScript object that has a type and payload (we do not need payload in this example because we are not passing any data). Also, you see that I create a constants.js file that stores all of our type variables. This is so that we won’t misspell the type names.
Now is where we create our reducer. Again, the reducer acts as a switch mechanism to facilitate the action that is being triggered.
The first thing to do when creating a reducer is that it needs an initial state. The initial state is similar to how you set state in React, it is simply how your state looks at the beginning. When an action gets triggered or dispatched, it calls the reducer function and passes the current state and the action object. So to facilitate which type to do what, the best convention is to use a switch statement, but you can also easily do it with if-else statements. In our case, we have a SET_INCREMENT
and SET_DECREMENT
type that will increase our counter state. In each switch case, we are just returning the states that have been adjusted as shown inSET_INCREMENT
and SET_DECREMENT
.
Ever since the release of React hooks, it makes the implementation of Redux exponentially easier. To start using the states and calling our actions, all we have to do is write a couple lines of code and we are on our way to accessing it. To do so, we make use of the useSelector and useDispatch hooks given by react-redux.
To get access to the counter state, all we have to do is use the useSelector hook. useSelector is a hook that takes in a function and we can specify which state we want it to return back. We can certainly just have it return everything that we have in our store, but in this case, we only need the counter state, so we specify it to be state.counter. To call our actions, we have to use the useDispatch hook. useDispatch will return back to a dispatch function that we can use to call our actions like the following dispatch(setIncrement())
.
Bam! That is all, and now your application is running on Redux!
But Wait…?
What if we need to run our action asynchronously? Like fetching data from an API. To do so, we need to add another element to our Redux store. We have to add a middleware called Redux Thunk.
Redux Thunk
Redux works in a synchronous way, so each action gets triggered in a sequence. If you would like to run an asynchronous task like fetching the data from an API, you would need to use an additional library called Redux Thunk. The library allows you to process asynchronous code by checking if your action is returning a function. This is very important to note as I described earlier that action is just plain JavaScript object. By applying the Redux Thunk middleware, every time an action gets triggered, it goes through the redux-thunk
first and it checks if the action is returning an object or a function. If the action is returning a function, it will insert a dispatch argument in your function which you can use to dispatch to return which type of action you would like for it to occur.
The only thing that is different from how we implement the store before is we imported two new components, thunk
and applyMiddleware
.
Let’s do a quick example of how we fetch some random quote from an API. The only major change is in the way we implement our actions. As explained above, our action now needs to return a function instead of an object in order for theredux-thunk
to intercept it.
There are many components in play here, let’s dissect each of them. So to reiterate again, when we are trying to perform an asynchronous action like fetching the data from an API, the action has to be returning a function. That is how the redux-thunk
middleware will be able to intercept and recognize that it is an asynchronous process. By returning a function, redux-thunk
gives you a dispatch method that will allow you to process a particular action type that was set in your reducer. You might be wondering why there are so many dispatches and action types. It is pretty much a convention to do those three types when you are doing an API call. The _REQUEST type is used to set the loader state to true. The _SUCCESS type is used to update the state from the data received from the API. The _FAIL type is used to indicate that there was an error in the call.
How we grab the data and dispatch our action is essentially the same as when we weren’t without using redux-thunk
. In this chunk of code, I’m just grabbing all the necessary states using useSelector. And I have a dispatch method that is used to trigger my fetchQuote action call.
The End
Redux is an amazing tool to have in your repertoire. It makes the process of managing your application’s state exponentially easier, especially with the addition of hooks. Redux allows you to take a peek at the entirety of your application’s state at any moment and know exactly what is going on. There are also many great tools that you can use alongside Redux to make managing your application’s state even easier. One of them is the Redux DevTools Chrome Extension. This tool allows you to visualize your application’s state in real-time and also go back in time if needed to debug any issues that you might have.
Thank you for taking the time to read through this. Please let me know if you have any questions. 🎯