Hooks are inarguably the future of idiomatic React. As a software engineering team lead, I am tasked with ensuring that my team stays current with the technologies of the day when they make sense. In the case of hooks, my team is already starting to adopt them in new projects. As such, I thought it would be useful to dive into some of the latest features that the community is forming around them.

The hooks paradigm allows all components to be functional components and the conversion from a stateless component to a component with local state is seamless. With the introduction of hooks to the react-redux module, the same can be said for store-connected components (or what we deem “containers”).

Consider a connected component in the pre-hooks paradigm:

Listing 1 — Connected component without hooks.

Because my team uses Typescript for ensured type safety, in this scenario we create a component, define an interface for types that have already been defined (in the redux action generator) that act as the type definition for the props of the component, and then we wrap the component in a higher-order connected component that takes two functions — one to map state to its corresponding prop interfaces, and one to map any functions that we want to dispatch to its corresponding prop interface. Not only are we re-defining our interfaces unnecessarily and writing extra functions to map entities to these interfaces (which we tend to re-write for every container that needs to use the same entities), but we are also polluting the virtual DOM with wrapped components that make it harder to inspect. Consider the virtual DOM for this container inspected with React tools in the browser:

Figure 1 — Virtual DOM with higher-ordered component wrappers.

Upon inspection of this virtual DOM, you can see we have the original provider that wraps the entire application to link the store to our application, followed by 2 connected components (containers), the first is the parent level app container and the second is our navigation container that was referenced in Listing 1. You’ll notice that each connected component creates 3 levels on the virtual DOM’s component tree: 1) The default export of the component after it has been wrapped by the context provider 2) the context provider itself coming from the “connect” function in react-redux 3) the actual component that you intended to be connected. As you can see, this pollutes the virtual DOM with potentially unnecessary components in the tree from a declarative programming perspective. This pattern also creates an opportunity for error if the component you import into your application is the actual base component instead of the default export created by wrapping that component with the higher-order connect component. So, can we do better?

React-redux has implemented new hooks in their library to replace this connect wrapper (ensure you have version 7.1.0 or greater installed and the installed type declarations match if you are using Typescript). They are the useSelector hook and the useDispatch hook. The useSelector hook returns the value from the store provided in the overarching Provider wrapper. This value will update any time the store updates and cause a re-render in your component. The useDispatch hook provides a dispatch function that is connected to the store provided in the overarching Provider wrapper. Using these hooks, we can simplify our connected component as shown below:

Listing 2 — Simplified component with react-redux hooks.

This code has much less boilerplate and is easier to reason about. It removes the possibility of importing the wrong component because there is only one exportable component in this file. You also do not have to re-declare the types for the selector and dispatch functions as they are implied from the action generators/selectors. Using hooks also provides the added benefit of easier refactor in the event you are using a non-connected component and suddenly need to convert it to a container. All you need to do in that scenario is to add the hooks that you want and you’re automatically connected. In this scenario the component tree in your redux tools will also be significantly cleaner by comparison:

Figure 2 — Virtual DOM with react-redux hooks.

This is obviously much easier to inspect.

Once these benefits were realized on my team, I took it one step further by proposing a new design pattern surrounding how to organize these react-redux hooks within a project. Using custom hooks, we have the ability to only have to write these selector/dispatch hooks once and then de-structure them into our functional components as needed. Consider these custom hooks created in a file called user.ts (which corresponds to the user actions file and the user reducer file) and placed in a hooks folder in the project:

Listing 3 — Custom hooks to centralize dispatch and selectors.

These custom hooks return objects with the react-redux hooks in them. We can de-structure these custom hooks into our connected components as necessary so that we don’t have to re-write them in every connected component. See our component refactored using these custom hooks:

Listing 4 — Connected component refactored with custom hooks.

As you can see this new connected component is much simpler, easier to convert to, less error-prone, and the selectors and dispatchers are much more modular. We only had to write the selector and dispatch hook once in our custom hooks and then we use the hook in our component and destructure just the selector and dispatch hook that we need.