Reusing logic in your React apps with Higher-Order Components

Precious Nyarko
7 min readNov 21, 2018

--

As you build React applications, you will run into situations where you want to share some functionality across multiple components. It could be that you you want a component to display a loading indicator, while it waits for some data to become available. Or perhaps, you need to access logged in user state from multiple components in your app. There’s got to be a way to share this state across those components, without having to basically copy-paste logic around, right? Enter Higher-Order Components (HOCs).

This post is a look at HOCs and how they make it easy to write easy-to-reason-about, reusable code. I hope to leave you with a good understanding of how HOCs work and how to start using them in your own code. Let’s get started!

What is a Higher-Order Component?

According to the React docs, a higher-order component is a function that takes a component and returns a component. Put differently, a HOC will basically take some component as an argument, (let’s call this the passed-in component from now on) and return a new component (let’s call this the enhanced component) that essentially wraps the passed-in component, with logic or functionality (via props), that it wouldn’t have had access to otherwise.

So whenever we want a component to access some reusable logic, we run that component through a HOC and render new component that gets returned.

Let’s consider a simple example to drive the point home. Say we want to be able to access the current date from several of the components in our app. Well, we could write const currentDate = new Date() in any component that needs it. But instead, let’s see how using a HOC would nicely abstract that logic away and make it available on-demand to any component that needs it.

We’ll call this HOC withCurrentDate. Here’s the code for it:

Let’s take a moment to understand what’s happening here. The withCurrentDate function is a JavaScript function like any other. It takes PassedComponent as an argument and returns a new component. This new component passes currentDate along with any other props it receives downPassedComponent. Okay, let’s move on.

With our withCurrentDate HOC defined, we can now use it, to make any component currentDate-aware, by simply passing the component as the argument to our HOC like so:

A more practical Higher-Order Component

The example above — while useful in demonstrating how a HOC works — , is a bit contrived. Let’s look at a more practical example. Say we have two components. The first — ContactList — renders a loading indicator, while it waits for contacts to be loaded from the randomuser.me API and renders list of contacts when the data arrives:

The second — GithubUserList — also renders a loading indicator, while it waits for user data to be loaded from Github’s API and renders list of users when the data arrives:

ContactList and GithubUserList aren’t identical — they load data from different endpoints, and they render different output. But much of their implementation is the same:

  • On mount, make a call the remote endpoint for data
  • Render loading text, while we wait
  • Call setState with the results, when the data comes back
  • Render a list of items with the retrieved data

You can imagine that in a large app, this same pattern of showing some loading text while we wait for data to become available will occur over and over again. What we want is an abstraction that allows us to define this logic in a single place and share it across many components. This is where higher-order components excel.

Let’s write a function that creates components, like ContactList and GithubUserList, that show a spinner while they make a request to a remote source for data and render that data when it becomes available. This function (which is our HOC) will accept as one of its arguments a child component that will receive the data once it arrives. Let’s call the function withLoader:

The interesting thing to note here, is that because we want withLoader itself to be reusable, we want to keep it as generic as possible. In other words, withLoader shouldn’t concern itself with how and from where the data is fetched. It is for this reason, that withLoader accepts a second argument:loadData, that handles the details of making the necessary API calls. This allows with loader to simply call fetchData and wait for it to return some data with which, which it then uses to update its local state.

With the introduction of withLoader we can now re-write ourContactList and GithubUserList as presentational components (i.e. simple components that simply accept some props and return some UI), since all the logic to deal with network calls, loading spinners etc has been nicely abstracted away. Plus we can reuse it anywhere we need that functionality.

Having been freed of the burden of dealing with network calls and loading spinners etc, our ContactList and GithubUserList components can now be re-implemented as simple presentational components — functional components that simply accept some props and return some ui.

Take a moment to compare these versions with the ones we started out with. That’s pretty damn sweet, if you ask me. Maintaining components like these is pure bliss.

Nice, but how do we actually use the withLoader component? Check it out:

We just call withLoader with the component that needs functionality — which in this caseContactList and a function to fetch the data we need — fetchRandomUsers in this case. What we get in return, is a new component — ContactListWithLoader that has all the functionality we need. Now, instead of rendering ContactList directly, we’ll simply render ContactListWithLoader and boom — done.

You can see the withLoader HOC in action here.

Thanks to higher-order components, we can enhance our simple functional components with whatever logic we want. This is the power of the HOC pattern. We can manipulate props or maintain internal state outside of our presentational components.

A few caveats to look out for

Higher-order components come with a few caveats that aren’t immediately obvious if you’re new to React.

1 — Don’t Use HOCs Inside the render Method

React’s uses component identity to determine whether it should update the existing subtree or throw it away and mount a new one. If the component returned from render is identical (===) to the component from the previous render, React goes the update route. If they’re not equal, the previous subtree is completely unmounted (and remounted).

You normally shouldn’t need to think about this, but in the case of HOCs, it matters because it means you can’t apply a HOC to a component inside the render method of a component, since you’ll be effectively creating a completely new component every time. What to do instead?

Better to apply HOCs outside the component definition so that the resulting component is created only once. This way, its identity will be consistent across renders. If you need to apply a HOC dynamically, you can also do it inside a component’s lifecycle methods or its constructor to achieve the same effect of preserving the component’s identity across renders.

2 — Static Methods Must Be Copied Over

If you have defined any static methods on your React component, the new container component that gets returned when you apply a HOC will not have any of the static methods of your original component.

To solve this, you could copy the methods onto the container before returning it like so:

However, this approach requires you to know exactly which methods need to be copied. You can use hoist-non-react-statics to automatically copy all non-React static methods:

3 — Refs Aren’t Passed Through

The key thing to note here is, if you pass a ref to a HOC it will not get passed through to the wrapped component. The ref will refer to the outermost container component, instead of the the wrapped component. This is because when React encounters a ref attribute on a component, it attaches the ref to the HTML element or component on which the attribute was set. This is not what we want. Fortunately, we can explicitly forward refs to the wrapped component using the React.forwardRef API. Learn more about how to do this in this post on using refs.

Interesting Aside: Usage out in the wild

I thought I’d take a moment to highlight out a good example of HOCs being used out there on the wild.

Redux, for example, uses a higher-order component returned by its, connect function to pass values from your application store to your components. It also does some error checking and component lifecycle optimisations that, if done manually would cause you to write a ton of boilerplate code. connect's typical signature looks like so:

It might not be immediately, obvious but connect is actually just a higher-order function that returns a higher-order component! If you break it apart, it’s easier to see what’s going on.

Conclusion

By now, you hopefully have good understanding of HOCs — the situations where they really come in handy and how you might use them to share functionality and simplify things in your projects.

Higher-Order components are a very useful pattern in React. When used correctly, they can lead us to write significantly less code, and components that are easy to reason about and maintain.

Photo by Ali Yahya on Unsplash

Congratulations! You made it to the end. I had a lot of fun writing this article. If you find out useful, share it with someone who might too. Cheers!

--

--