Photo by Joel Filipe on Unsplash

Higher Order Components: Theory and Practice

--

What I enjoy the most working with React.js is its functional approach for building UI’s. Everything you see on a screen is essentially a component. Components are composed of other components, making complex interfaces possible.

Higher-order components concept goes back to higher-order functions, functional programming concept, describing the function that takes other function(s) and returns a function. In exactly the same way, higher-order component takes another component(s) and return a component.

Where might it be useful? Let’s consider a few practical cases.

Web applications have different areas, which are accessible depending on user’s authentication and access control.

There are guest routes, like /signup, /signin, /forgot-password etc, as well as private routes, /dashboard, /user-profile and so on.

For example,

What we want to do: Everything under / URL is protected from unauthorized access.

One solution could be to extend App itself, with componentDidMount and componentDidUpdate and check if the user is authenticated. If not, redirect the user to /signin. It sounds right from the very beginning, but this solution has two drawbacks.

Firstly, it violates the single responsibility principle, what now mixes up functionality to App component, that originally doesn’t belong to it. And secondly, in case we want to introduce another route with similar behavior, that logic has to be present there as well, that would lead to some code duplications.

Let’s introduce a higher-order component for that,

We are using Redux here, but in general, the concept doesn’t depend on Redux at all.

So, requiresAuth is a function that takes Component and returns AuthenticatedComponent. AuthenticatedComponent wraps the original component, plus it checks if the user already authenticated, in case he is not, it will redirect to /signup.

Updated routing,

By extending requiresAuth with additional parameters, it’s very easy to add some ACL functionality,

Now, _checkAndRedirect() function is modified to take a role into account,

Redirections

Another case is a redirection of a user. A redirect is forced for some users in specific conditions. For instance, we might want a user to complete some actions during his onboarding, and prevent access to a particular route, if these actions are not completed.

Otherwise, if onboarding is completed, we want to redirect a user to the dashboard.

To make it possible, we introduce checkBoarding() function and a corresponding CompletedComponent.

Routing,

Setting up context

Some routes (or more typically, group of routes), require some data context. Say, /website/1 and /website/1/banners, /websites/1/revenues all requires that website with id:1 is loaded and available as part of the state.

Instead of loading the same data in each component, we might use higher-order component.

Routing,

Compose all the things

We sometimes need to apply a few higher-order components on the same route. As from routing above, we can see that /app route both requiresAuth() and checkBoarded(). Sooner or later, you might have 3-4 function calls there; that makes Routes a bit bulky.

Fortunately, higher-order components are easily composable,

composeComponents() is a simple reducer utility function,

So, higher-order components is a great concept that aims to replace Mixins as composition mechanism. My experience showed they are great as containers wrappers, but in general, they have many practical applications.

--

--

Alexander Beletsky
Product & Engineering at blogfoster

PhD, Entrepreneur, Learner. Fullstack JS Developer @blogfoster, Blogger, Data Science and Cryptography Enthusiast. Founded @likeastore, @socialsearchio.