Redux Using Hooks

Redux already makes it easier for us to access state in our React Components, Hooks takes it one step further.

Osha Groetz
8 min readMar 13, 2021

Redux is a state container for Javascript applications. Redux keeps our application’s states in one, centralized location which is not only cleaner, more organized, and easier to understand, but it makes debugging our state problems waaaay easier and it makes it quicker and easier to scale our apps, when needed. Without Redux, we have the nasty task of passing state to our child and grandchild components from parent components as props and having to follow the trail through each component when we need to pass the props (for example in call-back functions) back up to our parents. With Redux we have the store (which holds our state), the actions (which define the type of actions (events) we are going to perform on our state), and the reducers (best thought of as functions that take in an argument(action) and then determine by use of switch statements what to do to our state dependent upon what action/type was given to our function as an argument).

I have some earlier blogs that start to cover React Hooks. Hooks like useState and useEffect are just 2 of the many hooks that allow us to work with state and lifecycle methods in our Functional components in React. Before Hooks, we had to use our Class components to access and manage state.

In 2019, after React added Hooks with v16.8.0, Redux released version 7.1 introducing the ability to use Hooks with the Redux Store. Previously with our React components, as seen in an example snippet below, we would have to import ‘connect’ from react-redux to wrap our components and then use the functions ‘mapStateToProps’ and ‘mapDispatchToProps’ in order to access the state being held in the Redux store.

With v7.1, Redux gave us two Hooks to replace connect, mSTP (mapStateToProps) and mDTP (mapDispatchToProps); useSelector and useDispatch. Our other components and files that we build for Redux will continue to be coded exactly the same way. Our index.js file (or app.js file, whichever route you choose to begin) will still import { createStore, applyMiddleware, compose } from ‘redux’, and import { Provider } from ‘react-redux’. You will declare the variable of store to send down to all components while you wrap your App with Provider and send along this store as an attribute; <Provider store = store >. You’ll continue to set up your reducer files with switch statements and cases, if necessary to handle different async methods, and if using Redux Thunk, you’ll set up your action files exactly the same to handle those async actions to your API. But your component, that used to look like the code above, will now look like something like this:

To build a simple counter, let’s step-by-step set up Redux and learn how we made the switch (← meant to be read as a Redux pun) from the first code to the last.

Install Redux and React Redux

In your terminal, first run these 2 commands:

npm install redux

npm install react-redux

Set Up Redux and Create Store

In your index.js file, along with all of your initial imports, you’ll need to import createStore from redux, and Provider from react-redux. Provider will wrap the entirety of your application and will allow you to eventually access your store (which will hold all of your state) in any of your components.

You’ll notice in the code below that we are also importing our Reducer on line 10 and using this to create the constant of our store that we pass onto our Provider.

To build our reducer, we must first create our action file.

Create Action File

Actions carry payloads (data) from your application to your store. They are plain JS objects that have a type attribute that point to the type of action that will be performed. We need actions to eventually pass to dispatch, but we’ll get to that part later.

In Your SRC folder, create a folder called ‘redux’. In redux create a sub folder called ‘actions’. Create a file called ‘counterAction.js’.

It is common practice/convention to set constants as our string of types (called ‘string constants’), we’ll do so as seen on line 2 and 3. Next, we’ll define constants of functions (Action Creators) that create an action object, setIncrement and setDecrement above.

Create Reducer

Reducers are functions that regulate changes to our application’s state. Reducers take in two parameters; initial state and an action.

As you can see above, our reducer sets an initial state constant where, initially, our counter gets set to 0. The reducer function uses a switch and case statement (really just a fancier if/else statement) to determine which action to take dependent upon which action constant was given to it as a parameter. If the action.type, for example, was SET_INCREMENT, the switch statement sends us to the case of SET_INCREMENT, where it makes a copy of our state, and sets the counter to the previous value of state and adds 1.

Using Hooks for Access To State In Our Component

Let’s head over to our component and set up a div to show our counter and to set up buttons that will perform actions that will either add or subtract from our state.

Above, you’ll notice, we are keeping our App component as a functional component. Import the two hooks, useSelector and useDispatch, from react-redux that we’ll need to make use of our reducer, and in turn have access to state. useSelector takes the place of our old function mapStateToProps. In simple terms, we tell useSelector to go get state and return to us the value of the counter in state. useDispatch will end up taking the place of the prior function mapDispatchToProps. useDispatch calls our actions for us, sending along that action type to the reducer, where the reducer, again, will use the action.type to determine which case statement it will defer to.

ASYNC ACTIONS USING REDUX THUNK

You might notice that currently, our program using Redux and we are creating state directly in our Reducer, right in the front end. More often than not, though we will need to deal with some type of data coming in from an external source, an API, whether it be one we created through a backend with a database and routes, or an API that was created by an outside source that we need to hook into (← yes, another pun!).

This is the point where Redux Thunk comes into play. Thunk is a middleware (Middleware is a software that acts as an intermediary between two applications or services to facilitate their communication.¹) that allows us to return a function from our Action creator. Up until Thunk, our Actions were returning a plain JS object with key of ‘type’ and value of whatever action we determined, ex: ‘SET_INCREMENT’. Because we need to work with data from a server, we need a way to successfully handle our async functions while calling on our data, without the reducer resolving our action before the async function has a chance to finish. Now, whenever we trigger an action, the action first goes through Redux Thunk to see if the action returns an object or a function. If our action returns a function, than Redux Thunk knows it is an async process, and must wait for the promise of the async function to finish before it continues on to our reducer.

Let’s now update our program to handle async functions and data storage from an outside API.

Install Redux Thunk

In your terminal, run this command:

npm install redux-thunk

Update Imports in index.js

Update your index.js file redux import to include ‘applyMiddleWare’ (line 7) and import thunk from redux-thunk (Line 9)

Create a New Action File for Posts

In your action folder, create a new file ‘postAction’ for getting Posts from an external API (https://jsonplaceholder.typicode.com/posts) .

In this folder, we will have declare 3 string constants: FETCH_POST, FETCH_POST_SUCCESS (for when our fetch requests are successful, our reducer will know it’s ok to update state with the incoming data), and FETCH_POST_FAILURE (for handling our catch statement, should an error occur in our fetch request, the reducer will act accordingly and not touch the state).

Set Up Our Post Reducer

Let’s set up our Post Reducer and handle those 3 actions that we had in our post actions.

Our initialState is a bit larger of an object this time around. This reducer is going to take the entire data object that comes from our GET fetch and if the FETCH_POSTS_SUCCESS action comes to the reducer, bc the fetch was successful, it will set the key of ‘posts’ to the value of an array of all of the data objects (posts) that came to us. The state of isLoading will come in handy for designing our site, and other functionality. The state of error will be handy for us to send error messages to our user, should the fetch fail and for what reasons.

It’s Time To Set Up Our Component To View Our Data

Heading back into our app.js file, we will do the same exact process of using our Hooks useSelector, useDispatch, as well as import our useEffect Hook from react, so that as soon as our app component loads, it will set off the chain of events with our action/reducer files.

If you have any questions(for example like why I am having to return state.postReducer.posts [combiningReducers in index.js, btw]) and would like to check out my repo, please find it HERE.

Redux & Hooks are awesome! Have a great week!!!

--

--

Osha Groetz

Software Engineer. React, Javascript, Typescript, HTML, CSS, Ruby, Ruby On Rails