Back

What are Higher-Order Components in React?

What are Higher-Order Components in React?

If you’ve ever considered it repetitive and cumbersome writing the same logic or having to fetch data in different components, then this article is for you. These problems are some of the basic challenges faced by React developers but can be solved using higher-order components.

One key use of HOC is that it allows you to abstract out the common functionalities of a component and build a function. With HOC, you can share logic across different components without having to rewrite it. HOC amplifies one of the best-known principles of programming called the don’t-repeat-yourself (DRY) principle, a very important principle to adhere to generally when writing code or building an application.

In this tutorial, we will start by looking at Higher-Order Components in React from a high level- what they are, why you should use them, and how to write yours. We will also cover different use cases with real-life examples alongside a few caveats or a few things to avoid when using higher-order components.

What is Higher-Order Component- HOC?

Higher-Order Component in React is a function that takes a component as an argument and returns a new component. It is the state-of-the-art technique in React for reusing component logic. The HOC adds additional data or functionality to an original component. These components are not part of the React API, but a pattern that comes to the fore from React’s compositional nature. In simple code it looks something like this:

const NewComponent = higherOrderComponent(originalComponent)

Here, the function called higherOrderComponent that takes an argument- originalComponent. higherOrderComponent is a HOC component that adds some additional functionality to originalComponent and returns a new component. In addition, the NewComponent can also be referred to as EnhancedComponent like so:

const EnhancedComponent = higherOrderComponent(originalComponent)

Here’s HOC in simple terms:

  1. It is a component
  2. It takes two arguments. The first argument is a component in which we want to add logic, and the second argument is data
  3. Then it returns a new component
  4. The returned component is the enhanced version of the component that we have passed.

Quite easy right? Now, let us dive into some facts about higher-order components and a basic example.

Facts about HOCs

  • One of the powers of the HOC pattern is that you can build up a simple functional component with whatever logic you want
  • You can update/edit or transform props outside of your present component
  • Just like JavaScript functions, HOCs can be pure functions that return a new component
  • With HOCs, components are not mutated or modified. Instead, components are created.
  • HOC is used to compose components for code reusability.

Example

Let’s see the simplest form of a higher-order component. Consider this example-

import React from 'react';

const updatedComponent = OriginalComponent => {
  class NewComponent extends React.Component {
    render() {
      return <OriginalComponent />
    }
  }
  return NewComponent
}

Here, we created an arrow function that accepts the OriginalComponent as its parameter while also returning a new component called NewComponent. Thereafter, we returned the OriginalComponent inside our render method and finally returned the NewComponent inside the arrow function.

As seen from our code snippet above, a HOC is a function- UpdatedComponent that accepts the original component- OriginalComponent and returns a new or enhanced component- NewComponent. This is the most basic example of a Higher Order Component

Open Source Session Replay

OpenReplay is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data.

replayer.png

Start enjoying your debugging experience - start using OpenReplay for free.

Use Cases

In this paragraph, we will discuss the different ways higher-order functions can be used. One of such include:

  • To conditionally render components: HOCs can be used to conditionally render components. For instance, there might be a case with many components, when they are to be shown if a particular criterion is met with the info that’s provided in their props. Since HOCs have access to the wrapped component props, we will have a reusable HOC that conditionally renders the component depending on the info provided.

  • Managing common user-interaction states Another use-case of HOC is that it can be used to manage common user-interaction states. It is common knowledge that users feel pampered when our application controls respond to something as small as their cursor movements. For example, a user-interaction state like the hover and focus effect is often displayed beautifully, without repeated handlers with each component, by abstracting to a HOC which may have an occasion hook to reinforce the wrapped component with a state-specific flag.

  • Providing components with specific styles HOCs can also be used to provide components with specific styles. From the foregoing (managing common user-interaction states), based on the flag that we get from the HOC, we may have flag-specific styles not just for user interaction states, but also data-specific ones. In addition, we can also have some more trivial styles like background color, font size, font-weight at multiple places if the need arises. These styles can be easily provided through a Higher Order Component by wrapping the component with one, that just augments props with the precise className(s).

  • Provide a component with just any prop This is a well-liked use case for HOCs. With HOCs, we can study our codebase and decipher what reusable prop is required across components. Then, we will have a wrapper HOC to supply those components with the reusable prop.

Building A Higher-Order Component

In this section, we are going to build our first higher-order component. Also, we are going to showcase the usefulness of the higher-order component. So we are going to be creating three Counters: CounterA, CounterB, and CounterC that will render a button and each of these buttons are going to have a corresponding counter within it, handled by its state. The idea is that whenever we click on each of the buttons, each of the Counter is going to increment.

All three component is going to have the same shared code and we are going to use a higher-order component to extract the common logic to make it a lot more simple in general.

Sounds interesting? Let’s get started:

On your IDE, create a folder and inside the folder, open your terminal and create a new react app using npx create-react-app . Inside of your src folder, create a component folder to house our three components. Inside the component folder, create three files called CounterA, CounterB, and CounterC like so:

Now let’s go ahead and write some code. In CounterA, let’s import React and create a class component called CounterA. Let’s call the render method and return a button which we will call CounterA and finally, let’s export our class component, CounterA.

Your CounterA.js fil should look like so:

import React from 'react';
class CounterA extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        return (
            <button>CounterA :</button>
        )
    }
}
export default CounterA;

Since the plan is to create three components with similar logic so we can use higher-order component to extract the common logic, let’s go ahead and replicate our code in CounterA inside both CounterB and CounterC.

Please refer to this repo for code snippets for our CounterB component and this repo for CounterC.

To see what our code looks like on the browser, let’s run npm start on our terminal to start our application. You should get something similar to this on your browser:

Next, let’s go ahead and implement some functionalities to our code in terms of clicking on the button and incrementing the count of each one. To start with, let’s define our state like so:

import React from 'react';
class CounterA extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            count : 0
        }
    }
    CountIncrement = () => {
        this.state.count++;
        this.setState({count: this.state.count});
    }
    render() {
        return (
            <button onClick={this.CountIncrement}>CounterA :{this.state.count} </button>
        )
    }
}
export default CounterA;
  1. We initialized our state inside our constructor and set count to 0
  2. We created a method called CountIncrement, incremented our count value using the increment method, and then we set our state to the new incremented value (still inside our CountIncrement method).
  3. Inside the render method, we called the onClick event listener on our button, then we render our state- this.state.count.

Go back to your browser and reload your page. Notice that on click of CounterA, there is an increase in its value!. Next, let’s update our CounterB and CounterC components with the code in CounterA. You should have the same code in all three components because they are all doing the same thing.

Obviously, there is a duplicity in logic in all three components. For example, there’s an increase count method in all three and the state is keeping track of the count in all three as well. This is where HOC comes in handy. To prevent a repetition of these sorts, we are going to extract the common logic in all three components using a higher-order component, such that we can reuse them (the common logic) when we need them.

Let’s take a look at how to actually implement a higher-order component. To begin, let’s create a file called Wrapper.js inside our src folder which will house our HOC. So, let’s begin:

import React from `react`;
const Wrapper = WrappedComponent => {
    class Wrapper extends React.Component {
        constructor(props){
            super(props)
            this.state = {
                count: 0
            }
        }
        CountIncrement = () => {
        this.state.count++;
        this.setState({count: this.state.count});
    }
        render () {
            return (
                <WrappedComponent
                    count={this.state.count}
                    CountIncrement={this.CountIncrement}

                />
            )
        }
    }
    return Wrapper;
}
export default Wrapper;
  1. We imported React as usual
  2. We created a stateless functional component which we called Wrapper.js which takes an argument called WrappedComponent
  3. Inside our Wrapper.js, we created a react stateful component called Wrapper.js, called the constructor function and super where we passed props as a parameter
  4. We then called this.state={count : 0} which we implemented in all three components. Note: this is where the abstraction comes into place
  5. Next, we called our render function and we rendered/returned the WrappedComponent which we gave some props: count and CountIncrement which we defined in lines 11-13
  6. At the end of the component, we returned our Wrapper.js component

This is essentially how you implement your higher-order component. Next, we are going to see how to utilize it.

Go back to your CounterA.js file and import our HOC using the syntax below:

import Wrapper from '../Wrapper';

In our CounterA.js, delete your state component and method because both are now being handled by our HOC- Wrapper.js. Let’s convert our CounterA.js into a stateless component and return the button. Also, delete the this keyword like so:

import React from 'react';
import Wrapper from '../Wrapper';
const CounterA = () => {
      return (
            <button onClick={CountIncrement}>CounterA :{count} </button>
        )
}

export default CounterA;

Wondering at this point how we are going to access those variables? We can access the variables from the props in CounterA and we can get those props by passing CounterA into our HOC component which is Wrapper.js. Also, we can go ahead and use object destructuring in JavaScript to get both the CountIncrement and the count from the state component.

Our CounterA.js file should now look like this:

import React from 'react';
import Wrapper from '../Wrapper';
const CounterA = (props) => {
       const {CountIncrement, count} = props; //getting those variables we were missing before
      return (
            <button onClick={CountIncrement}>CounterA :{count} </button>
        )
}

export default Wrapper(CounterA);

Now, let’s rerun our code to see if our CounterA still increments on click of the button.

Viola! It still does. Super amazing!!!

So, we have successfully used higher-order components in our CounterA.js file to get rid of a lot of code that we didn’t need - functionalities such as the method we are using and the state we are keeping track of.

Kindly refer to the Github repo to see how your CounterB and CounterC component should look like.

Things to avoid when using Higher-order component

As with many concepts in programming, there are certain things to avoid when using HOC. A few of these caveats include:

  • Avoid using HOCs inside the render method In React, component identity should be consistent across renders. This is because React’s reconciliation process uses component identity to decide whether it should update the prevailing subtree or mount a replacement between two renders. For example, using HOCs inside the render method cannot preserve the identity of HOCs across render and this invariably affects the overall app performance. Also, when a component’s state changes, React has got to calculate if it’s necessary to update the DOM. It does this by creating a virtual DOM and comparing it with the present DOM. during this context, the virtual DOM will contain the new state of the component. As a best practice, we should apply HOCs outside the component definition so that the resulting component is created only once.

  • Refs are not passed through While you may want to pass all props to the wrapped component, you should pay particular attention to refs since they’re not really props. Refs are handled specially in React like key. For instance, If you add a ref to an element whose component is the result of a HOC, the ref refers to an instance of the outermost container component, not the wrapped component. To solve this problem, use the React.forwardRef API.

Conclusion

In a nutshell, higher-order components are useful and important because they extract code away from components that are using the same code. So, it is essentially solving the problem of repetitiveness in our code.

In this tutorial, we have looked at higher-order components and how we can use them in our code with real-world examples. We also covered use cases of higher-order components, facts, and of course some caveat/things to avoid when using higher-order components.

Please see below a link to the code examples on Github:

Further resources

Refer to the React documentation if you want to dive deeper into some of the things we discussed above.