Geek Culture
Published in

Geek Culture

What is a Higher Order Component?

A detailed explanation of React’s system for reusing component logic

Bruce Wayne is enhanced by his Batsuit like a Higher Order Component enhances a normal component (Source: https://chrisnolan.fandom.com/wiki/Bruce_Wayne)

Have you ever been working in a codebase or been watching a tutorial online and come across something that looks like this:

export default withResponsive(Home)

Well then you already have encountered a Higher Order Component whether you’ve realized it or not.

A Higher Order Component (HOC for short) is React’s way over giving multiple components some repeated functionality or behavior, without having to reuse the same code over and over again in the component. Its a very clever way to follow the DRY (Dont Repeat Yourself) motto and cut down on code clutter.

A HOC is a function that takes in a component as an argument and returns a new enhanced component.

const EnhancedComponent = higherOrderComponent(OriginalComponent)

In the title image of this article I used the example of Bruce Wayne versus Batman. The main difference between the two is that Batman is an enhanced version of Bruce with a fancy suit and tons of gadgets he uses to fight crime. In this example Bruce is the original component, and Batman is the enhanced component. His suit (and gadgets and Batmobile) are the HOC.

const Batman = withBatSuit(BruceWayne)

The HOC withBatSuit will give Bruce access to his BatSuit object and turn him into Gotham’s own Dark Knight.

So how does it work exactly and when would it be appropriate to use one?

When To Use a HOC

Let’s say you have a project where you have multiple different counters counting some behavior. In one component, you have a button that counts the number of times it is clicked, then in other component you have a counter tracking the number of times you mouse over a certain div, then in yet another you are counting the number of times a certain page is visited. All of these components will have similar behavior (incrementing the count by 1 after some specific action) but since they are all slightly different we unfortunately can’t make then all the exact same component.

Now, before HOCs you would have to write the incrementCounter behavior into each one, and then have some sort of state variable that tracks the count. This is a lot of copy and pasting and repeating code though. What if we could give each component the incrementCounter function and also a state.count variable and then only tweak the action that trigger the incrementCounter function in each?

That’s exactly what we can do!

Another use case would be for something like determining how to display a page based on if a user is accessing it from his laptop, phone or tablet. If your website is mobile responsive, there is a pretty common pattern when you pass the component an object called isResponsive which is a boolean indicating true or false. Rather than passing this as props down to each component that needs access, you can render the component using a HOC called withResponsive that will give each component access to the isResponsive object dictating how it should be displayed to the user.

These are 2 common uses, but you can use it literally wherever you need multiple components to share some sort of behavior.

Implementing a HOC in your App

Let’s go back to our first example of adding a counter to multiple components in your app.

If we were to hard code the counter behavior into each app you’d end up with something like this:

App.js

function App() {return (
<div className="App">
<ClickCounterWithoutHOC/>
<HoverCounterWithoutHOC/>
</div>
);
}
export default App;

ClickCounterWithoutHOC

import React, {useState} from 'react'const ClickCounterWithoutHOC = () => {  const [count, setCount] = useState(0)
const incrementCount = () => setCount(prevCount => prevCount + 1)
return ( <button onClick ={incrementCount}>
Clicked {count} times
</button>
)
}

export default ClickCounterWithoutHOC

HoverCounterWithoutHOC

import React, {useState} from 'react'const HoverCounterWithoutHOC = () => {  const [count, setCount] = useState(0)
const incrementCount = () => setCount(prevCount => prevCount + 1)
return (<button onMouseOver ={incrementCount}>
Hovered {count} times
</button>
)
}
export default HoverCounterWithoutHOC

As you can see. This pattern is fine and it works, but we are repeating a lot of code between HoverCounter and ClickCounter. Now imagine a scenario where there are 5 or even 50 other components that need this counter behavior and all of a sudden you have a very inefficient codebase.

It works!

Instead let’s create a Higher Order Component called withCounter.js that will add this count and incrementCount behavior for us.

withCounter

import React, {useState} from 'react'const withCounter = OriginalComponent => {
const EnhancedComponent = () => {
const [count, setCount] = useState(0)
const incrementCount = () => setCount(prevCount => prevCount + 1)
return ( <OriginalComponent
incrementCount ={incrementCount}
count={count}
/>
)
}
return EnhancedComponent
}
export default withCounter

You’ll see that what this component does is move the declarations of count and increment count into withCounter.js, and then it returns an EnhancedComponent which is just the OriginalComponent with incrementCount and count given to it as props.

Pretty simple concept once you see it in practice. Let me repeat that again.

A Higher Order Component is just a function that takes in a component and enhances it by passing some behavior to it as props. The Higher Order Component then returns that new, enhanced component for you to use.

Now let’s see how this affects ClickCounter and HoverCounter

ClickCounter

import React from 'react'
import withCounter from './withCounter'
const ClickCounter = ({count, incrementCount}) => { return (
<button onClick ={incrementCount}>
Clicked {count} times
</button>
)
}
export default withCounter(ClickCounter)

HoverCounter

import React from 'react'
import withCounter from './withCounter'
const HoverCounter = ({count, incrementCount}) => {return (
<button onMouseOver ={incrementCount}>
Hovered {count} times
</button>
)
}
export default withCounter(HoverCounter)

Look how much cleaner this is compared to the counterparts about without the withCounter HOC.

All we need to do is import withCounter into each component, and at the bottom wrap our export with withCounter. This will export the enhanced version of our component.

Then, all we do is destructure our props, giving us access to count and incrementCount in the body of our JSX and we’re done!

Make sure you’re App.js file looks like this so you can test it out:

App.js

function App() {return (
<div className="App">
<ClickCounter/>
<HoverCounter/>
</div>
);
}
export default App;
Works exactly the same as before but much cleaner.

And there you have it! Here is the link to my GitHub Repo if you want access to any of the code from this article to play around with for yourself!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store