How I reduced the code in my Redux app by using Redux Hooks.
By: Sunil Sandhu
Tags: JavaScript, Programming, Web Development, Coding, React

How I reduced the code in my Redux app by using Redux Hooks.

Here’s how you can simplify the Redux bindings in all of your components to help reduce your React Redux boilerplate.

I recently wrote an article that compared the exact same app built with React and again with React + Redux.

The code for the app that had Redux used connect() bindings to hook up our components to our store. This meant that we had things such as mapStateToProps and mapDispatchToProps adding several lines of code to each of our container* components.

*A container component is typically an entry point for our Redux store. These files would then pass Redux store state down to subcomponents as props. In our example, our container component is App.js. But if you were using React Router, you may decide to treat each route (eg. Index.js, About.js etc) as a container component. Use of the word “container” is simply a semantic word to help a developer differentiate it from a ‘regular’ component. There is no difference in how either component is created, although you may choose to put your container components inside of a container folder in your app directory if you choose to.

Anyway, a couple of people mentioned that I should have used Hooks to simplify those Redux bindings.

Now please bear in mind that this isn’t an article designed to preach that you should go and rewrite all of your previous Redux code — because you definitely shouldn’t be wasting time rewriting something if it already works perfectly. However, I do think it would be a good idea to shed some light on what your code would look like if you swapped out the old Redux bindings for the new Hooks-based ones. That way you can look to leverage these new bindings for the next new project you find yourself working on.

Note: We won’t be deep-diving into the code used in our actions, reducers or store (or any of the other files that usually sit inside of the redux folder). As this is a continuation of a previous article, we recommend that you go and read that article if you are looking to understand how the lower-level parts of Redux work. The link can be found here.

Note: You will still need to wrap your app with the react-redux <Provider> component. For reference, this is what our src/index.js file looks like:

import React from "react";

import ReactDOM from "react-dom";

import { Provider } from "react-redux";

import configureStore from "./redux/store/configureStore";

import App from "./App"; const store = configureStore(); ReactDOM.render(

<Provider store={store}>

<App />

</Provider>,

document.getElementById("root")

);

Let’s start!

Now our To Do app is pretty simple, so fortunately we only had one file that needed to use our Redux connect() bindings. That file also didn’t have to pull in too much stuff from our Redux store/actions. However, the file still looks quite messy, as we can see from the image below:

Our app when using connect() bindings

Here’s what our new App.js file looks like with the our Redux Hooks bindings:

Our app when using Redux Hooks

One immediate difference between the two screenshots here is that everything is separated out in the first screenshot where we used the connect() bindings, whereas everything is encapsulated inside of our App variable in the second screenshot when we use Redux hooks. This encapsulation is required as, quite simply, we cannot use hooks outside of a React functional component.

So with that small matter out of the way, let’s go through those five lines of code inside of App to understand how this all works:

useSelector()

useSelector()

useSelector() is a hook that provides access to our Redux store’s state. This hook takes a selector function as an argument. The selector is called with the store state. So here we called our argument store and then accessed our appReducer which had a list object inside of its state. If you’re curious to understand how those parts connected, I suggest reading the previous article that covered this. By using useSelector() , we effectively replace the need to use mapStateToProps() , enabling us to directly hook into the Redux store without needing to pass state as props from a higher-order component.

In short, this hook creates a reference to that particular part of our state. We can then pass this around our app and whenever it gets updated by Redux, our reference gets updated as well.

Okay, so we have access to our state through hooks, but what about access to our dispatch functions?

useDispatch()

useDispatch()

useDispatch() is a hook that provides us with access to the redux dispatch function. In the screenshot above, you can see that we assign useDispatch() to a const called dispatch .

We then create two more const 's that have identical names to the dispatch functions we want to create references to. You could choose to call them whatever you want, but this is the path I chose to take:

Our dispatch functions

This may look a bit confusing at first, so let’s break it down further. We are creating functions that take in a param, todo in the redux_add function and id in the redux_delete function. These are basically the payloads that our dispatch functions require. Then our functions return the dispatch that had been initially been given the value of useDispatch() .

As it is a reference to the Redux dispatch function, we basically just pass in the function that we wanted to call, which in the case of redux_add , is the redux_add function that sits inside of appActions , which was imported at the top of the file. You can also notice that we pass in the todo param as its payload. We do the same thing with the redux_delete function.

Now there are other ways that you could choose to write this. For example, I could have written this as:

An alternative way to writing our dispatch functions

This achieves the exact same thing and is basically what you would end up finding inside each function in the appActions.js file. If you choose to take this second route, be sure to import your Action Types instead of your App Actions, which means importing this:

importing actionTypes

instead of this:

importing appActions

In short, we use useDispatch() to create local references to specific dispatch functions. This replaces our need to use mapDispatchToProps() .

And then how do we pass these values to our components?

Props

How we passed in our props to the ToDo component

This final bit wasn’t wholly necessary, as I could have just passed each item into the <ToDo/> component like this:

An alternative approach to passing in our props.

But I generally don’t like how this looks and wanted the code for the props object for both the connect() bindings version and the Redux Hooks version to look as similar as possible - this was achieved by creating the props object .

So there we have it!

We’ve reduced some of the boilerplate in our Redux app. Besides the Redux mental model, the boilerplate can be a concern for new devs to wrap their head around. Hopefully Redux Hooks can help to reduce the amount of head wrapping. We no longer have to worry about wrapping our components inside of connect() Higher Order Components anymore!

Now you can focus on wrapping your head around more pressing concerns like asynchronous action creators!

The original article that compared React with React-Redux, it can be found here: https://medium.com/javascript-in-plain-english/i-created-the-exact-same-app-with-react-and-redux-here-are-the-differences-6d8d5fb98222

If you’d like to review the repo used in this article, it can be found here: https://github.com/sunil-sandhu/redux-hooks-todo-2019

You can also compare it with the original Redux repo that used the old bindings here: https://github.com/sunil-sandhu/redux-todo-2019