Shane Brinkman-Davis Delamore is a master of the craft and passionate about UX design and programmer productivity.
Updated Nov 19, 2019
Original post: Medium.com/@shanebdavis
Redux is great. It lets you persist, restore, log, and investigate your application’s state in a reliable, predictable way. Further, there are lots of great libraries out there like react-redux, redux-devtools and redux-logger which make working with Redux a pleasure… almost.
There’s just one thing. Redux takes an excessive amount of boilerplate to code. The standard way to use Redux is a highly manual, redundant process of dispatching, routing and reducing your state. But it is just that: standard — and that means automatable.
After working on several projects using Redux, and noticing these patterns, I started designing a tool to automate it. I’m not the first person to observe this: reduce-react-boilerplate, reducing-boilerplate-in-redux and minimize-redux-boilerplate-with-4-lines-of-code, nor the first to try to address this: reduxless, redux-actions, redux-arc and many more. However, these previous attempts each fail in one of two ways:
So I created hooks-for-redux. I liked the way hooks cleaned up React, so I decided to use a similar API to clean up Redux. I’ll explain how H4R works with an example:
I recently read Sunil Sandhu’s great post detailing how well React-Hooks cleans up how you use Redux in components. I‘m going to use his result as my starting point:
For those who don’t want to wait, you can see the final results here:
Before we dive into the code it is useful to understand the essential logic of the app. An app can be no simpler than its essential logic. That gives us a target to strive for as we attempt to clean up and reduce the code size of the To-Do app.
Starting with the user-interface, the React components pseudo-code might look like this:
As you can see, there are just two components. ToDo consists primarily of a list of ToDoItems, one for each item from useList. The ToDo component also has a text-box and a button for adding new ToDo items via addItem. ToDoItems show each item’s text and a deleteItem button.
Understanding the essential logic of an app gives us a target to strive for when cleaning up code.
(This psuedo-code is almost functional except addItem and deleteItem aren’t getting passed the correct parameters.)
The rest of the application is managing the todo list itself. Here’s the pseudo-code:
At the top of the pseudo-code you can see the structure of the list and its initial state. Lines 7 and 8 describe the logic behind our two list operations: addItem and deleteItem. These are expressed as reducers — they take the current list’s state, plus an item, and return a new list with the change applied.
Last, there are three stubs for the three functions used in the components above:
The entire, near-working essential logic of the ToDo app is just 30 lines of code. Real apps have more code to manage styling and robustness, but the goal is to get as close to this minimal form as possible.
At the top of the pseudo-code you can see the structure of the list and its initial state. Lines 7 and 8 describe the logic behind our two list operations: addItem and deleteItem. These are expressed as reducers — they take the current list’s state, plus an item, and return a new list with the change applied.
Last, there are three stubs for the three functions used in the components above:
The entire, near-working essential logic of the ToDo app is just 30 lines of code. Real apps have more code to manage styling and robustness, but the goal is to get as close to this minimal form as possible.
I’m going to take it one file at a time starting with the root index.js. I’ll show the before and after and explain what changed.
(I’m using lines to measure code size. Though it’s not my preferred method, it is easiest. Read: how to count code in tokens.)
This is the root file for the application.
index.js before (14 lines):
index.js after (11 lines):
You’ll notice two changes to src/index.js. First, there is no call to configureStore, and second, there is a new Provider which doesn’t require a store parameter. Hooks-for-redux is designed to add reducers to an existing store rather than require all reducers be defined before the store is created. This allows us to use a default store, which is sufficient for many applications. That way you don’t have to explicitly create it or bind to it throughout your application. Of course, you can override the default store as needed.
App is the root React Component for the application.
App.js before (15 lines):
App.js after (5 lines):
Simplifying App.js is pretty straight forward. App.js shouldn’t be dependent on the List state at all. Among other problems, that unnecessary dependency causes App to re-render whenever the list changes, which is wasteful. Only ToDo needs to re-render in that case. In general, always minimize dependencies between modules.
ToDo is the main component for presenting the todo-list. This is the biggest JavaScript file, and there are many changes. Most are small improvements in how React is used. Some of these are subjective, but my goal is always to reduce code without sacrificing clarity.
ToDo.js before: (72 lines)
ToDo.js after: (42 lines)
React code cleanup:
There are more substantial changes as well:
These changes are pretty straight forward:
ToDoItem.js before: (17 lines)
ToDoItem.js after: (11 lines)
Now for the real magic. H4R’s useRedux eliminates all the boilerplate needed to manage your Redux store, and it does this without sacrificing any power or compatibility. It allows you to put all your Redux logic in one file.
(Note, on larger projects I recommend creating one file per data-model. In this simple app, though, there is only one data-model: list.)
5 redux-related files before: (61 lines)
Just list.js after: (16 lines)
getUniqueId is basically the same as generateId originally in ToDo.js. I moved it out of the React-view and into the Redux-model where it belongs.
The rest of the file is the call to useRedux. Each call to useRedux defines a slice of named Redux state. It takes three main arguments:
And it returns, ready to use, an array containing the following:
(Note: There are some additional inputs and outputs not shone in this example. Refer to the README for more.)
This is how hooks-for-redux works. It takes the essential information needed to define your state — name, initial state and reducers, and it returns the essential methods for interacting with that state. Internally it’s doing exactly what you’d be doing manually yourself — in a clean, efficient and most importantly, well-tested way.
The original source was 177 lines of JavaScript. With hooks-for-redux, it streamlined down to just 85. That’s 48% the size of the original line-count. We also reduced the number of files from 9 down to 5.
How did we do compared to the 30-line essential solution? If you merge the H4R version into one-file you can remove most of the import and export lines. If you further remove all the styling, it slims down to just 45 lines. Compared to the essential, not-quite-working solution, the extra 15 lines account for handling text input, initializing the React app and a few other details required to make it actually work. I don’t think you can make it much simpler:
Redux is a great extensible, client-side state management platform. I want to give plenty of credit to Dan Abramov and Andrew Clark. Redux’s extensibility is what makes H4R possible.
However, Redux falls short from the application-author point of view. The standard pattern for using Redux is highly repetitive. Not only does it take more time to write the same code over and over, but it also creates more surface area for bugs to occur. The standard pattern litters dependencies all over your code and encourages mixing your Redux logic with your React components.
Almost all good software engineering comes down to one thing: Don’t Repeat Yourself. This is what hooks-for-redux does for you. It eliminates the repetition without sacrificing any power. You still define your state. You still define your reducers, but the rest is taken care of for you.
Some more of my thoughts about DRY and writing less code can be found here.
Best of all, H4R is small. The core function, useRedux, is about 10 lines of code, and the entire library is just 90. That means, even with a tiny project like this, not only is your source code smaller and more robust, but your total code-size is also smaller. The savings only grow as your project grows.
Can we help you apply these ideas on your project? Send us a message! You'll get to talk with our awesome delivery team on your very first call.