Note: Good knowledge of React is required.
So what is a Higher Order Component ( HOC ) in React?
According to the official documentation:
A higher-order component (HOC) is an advanced technique in React for reusing component logic. HOCs are not part of the React API, per se. They are a pattern that emerges from React’s compositional nature. Concretely, a higher-order component is a function that takes a component and returns a new component.
So to sum up all this, a higher-order component is a function that takes a component and returns a new component.
It is a pattern created out of React’s compositional nature so basically incorporate the don’t-repeat-yourself (DRY) principle of programming.
Here i will try to give a very simple coding examples and the explanation.
If you have ever used Redux or React router libraries, you must have came across in something like this:
>> export default connect(myComponent) or export default withRouter(myComponent).
Well those are some Higher order components that wrap your component and gives them some extra functionality.
So lets start with the classic counter example which is very easy to grab an idea.
We create 2 files, CounterClick.jsx and HoverCounter.jsx
CounterClick.jsx:
Here we are displaying a button and a counter for how many times we click it.
We will get the 'count' and 'incrementCount' from our HOC component ( we create this in a little bit).
So those are just props that we pass from our HOC to the component we want to consume them. In this case in ClickCounter component.
import React from "react";
import withCounter from "./withCounter";
const ClickCounter = ({ count, incrementCount }) => {
return (
<button onClick={incrementCount}>
Clicked {count} times
</button>
);
};
export default withCounter(ClickCounter);
and HoverCounter.jsx:
So here the same as above, we just consume the props we get from our HOC. Same props! The difference here is that those props change not by clicks but by mouse hover in the component.
import React from "react";
import withCounter from "./withCounter";
const HoverCounter = ({ count, incrementCount }) => {
return <h2 onMouseOver={incrementCount}>Hovered {count} times</h2>;
};
export default withCounter(HoverCounter);
we also create our HOC in a new file called withCounter.jsx :
Here we use the logic that we would use in each component if we didn't create our HOC.
We save our count state and the function that changes out count state 'incrementCount'.
As you see, we take as argument a component here WrappedComponent and return a new updated component withCounter.
withCounter will be used as HOC in every component we need the logic that this HOC holds( in our case only the count and incrementCound function).
In the WrappedComponent you can see we pass some things as count and increaseCount that we use in the previous components, but what is that {...props}?
This is extremely important because if we wanted to pass some data as props from our parent in the App.js example name="Some Awesome Name" to our Counter component, well you will notice that we will not see anything there. But why cant we see anything ?
because our prop we pass from the App.js (where we use out components to be rendered in the screen), the prop goes to out HOC and from there we can pass it down to the children. This is why we spread the props like {...props}. In this way we will not lose any data.
By convention we start our HOC with lower keys "withSomeName".
import React, { useState } from "react";
const withCounter = (WrappedComponent, incrementNumber) => {
const WithCounter = (props) => {
const [count, setCounter] = useState(0);
const incrementCount = () => {
setCounter((prev) => prev + 1);
};
console.log("HOC");
return (
<WrappedComponent
count={count}
incrementCount={incrementCount}
{...props}
/>
);
};
return WithCounter;
};
export default withCounter;
and finally we call those 2 components HoverCounter.jsx & CounterClick.jsx in our App.js
import React from "react";
import Counter from "./counterClick";
import Hovered from "./hovereClick";
function App() {
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "600px",
}}
>
<Counter name="Some Awesome Name" />
//some margin to push out component a little to the right
<div style={{ marginLeft: 50 }}>
<Hovered/>
</div>
</div>
);
}
export default App;
But wait.. We can do more cool things with our components.
In our HOC we can add another parameter besides the OriginalComponent, we can add anything we want.
Example:
So we don't increase by one, by we will increase our count, by anything we define in our Original Components that get Wrapped by this HOC.
import React, { useState } from "react";
const withCounter = (WrappedComponent, incrementNumber) => {
const WithCounter = (props) => {
const [count, setCounter] = useState(0);
const incrementCount = () => {
setCounter((prev) => prev + incrementNumber);
};
console.log("HOC");
return (
<WrappedComponent
count={count}
incrementCount={incrementCount}
{...props}
/>
);
};
return WithCounter;
};
export default withCounter;
and in our Original component, we add a second value like this:
import React from "react";
import withCounter from "./withCounter";
const ClickCounter = ({ name, count, incrementCount }) => {
return (
<button onClick={incrementCount}>
{name} Clicked {count} times
</button>
);
};
export default withCounter(ClickCounter, 10);
You noticed that if we try to click , our counter will be increased by 10 now as we defined here.
Conclusion: There are better ways to implement than a Counter component as i did here, but this was just a very basic example to get an idea about HOC. For example we can implement a loader that shows a loading component before a completed fetched data is rendered or for Authentication which is a little more complex to explain in this blog and i don't wait to make it longer.
those are some advanced patterns in React that we can get huge benefits if we have some logic that we need to implement in many components and we don't want to use props in a way that we nest 10 levels down.
Together with custom hooks( witch i explained a blog before Custom Hooks), it can make a great deal in your code base and make things much easier to work around.