Higher-Order Components are actually a design pattern derived from the compositional nature of React, which allows us to derive components based on particular data we want to pass down to them.
Think of it this way:
Don’t get confused — a HOC itself is not a component.
Remember: Components are functions that return a React element (JSX Element), higher-order components are the functions that return those components.
At a higher level of abstraction, an HOC would look something like this:
const withHOC = (Component, someData, otherArgs) => {
return (props) => {
return (
<Component
customProp={someData.firstProp}
dataSomething={otherArgs.someOtherDataProp}
{...props}
>
<h2>Might as well add nested elements</h2>
</Component>
);
};
};
with …
symbolizes the defacto (the most commonly used) naming convention when working with HOCs
And a non-practical example of how this would be used:
import withHoc from 'previous-gist';
const Component = (props) => {
return (
<div>
<h2>Hello!</h2>
</div>
);
};
export default withHOC(
Component,
{ // `someData` argument
prop1: 'what's good?',
prop2: { propProperty: 'holding some data' },
},
{ // `otherArgs` argument
arg1: { property: 'some-value' }
},
)();
Now that we’ve gotten a bit more familiar with this concept, let’s go into a more realistic scenario where you might find yourself in need of HOCs.
Folder Structure:
You can check out the repo here: https://github.com/Vlad-Mihet/React-HoCs for the entire project file system structure.
src/api
: will contain the stored blog posts, with an afferent getter method for retrievalsrc/components
: will contain generic components; in our case, it will be BlogsContainer
: which will act as a presentational component, and only display the blog posts passed down to itsrc/views
: will contain the views of the application; in our case, it will only be the Home
view, which will render a list of Recent, Popular & Archived blog posts through the container components in src/views/home/components
src/views/home/components
: will contain Home
view related components; in our case, these will be all container components: ArchivedBlogs
, PopularBlogs
, and RecentBlogs
, which will all be responsible for fetching their own datasrc/App.js
: will embed the Home
viewWell, we do have different components for a rather similar task: Initializing a piece of state for blogs, receiving the same blogs
prop, updating the blogs state at mount time, and rendering it with the same BlogsContainer
component.
It is code duplication that could be solved through the implementation of a Higher Order Component.
Create an HOC withBlogs
under src/hocs
with the following content:
import { useEffect, useState } from "react";
const withBlogs = (Component, retrieveBlogs) => (props) => {
const [blogs, setBlogs] = useState([]);
useEffect(() => {
setBlogs(retrieveBlogs);
}, []);
return (
<Component blogs={blogs} {...props} />
);
};
export default withBlogs;
The withBlogs
HOC would get a Component, as well as a blogs retrieval method as parameters, and will be tasked with fetching the blogs data and returning a component with the blogs
prop set as the retrieved blogs data.
Next, in the Home
view, we could discard the components we have used from src/views/home/components
, and define the new components created by our new HoC:
import React from 'react'
import { getArchivedBlogs, getPopularBlogs, getRecentBlogs } from '../api';
import BlogsContainer from '../components/BlogsContainer';
import withBlogs from '../hocs/withBlogs';
const RecentBlogs = withBlogs(BlogsContainer, getRecentBlogs);
const PopularBlogs = withBlogs(BlogsContainer, getPopularBlogs);
const ArchivedBlogs = withBlogs(BlogsContainer, getArchivedBlogs);
const Home = () => {
return (
<div className='home'>
<section>
<h2>Recent Blogs</h2>
<RecentBlogs />
</section>
<section>
<h2>Popular Blogs</h2>
<PopularBlogs />
</section>
<section>
<h2>Archived Blogs</h2>
<ArchivedBlogs />
</section>
</div>
);
};
export default Home;
Not only have we reduced code duplication, but we have also implemented a simpler and more easily-readable solution.
Now, you might be wondering, why don’t we simply use the BlogsContainer
component inside the withBlogs
HOC, rather than using a generic parameterized component?
Well, what if we ever want to maintain the same functionality, but in another case? For example, we might have a banner component that fetches blogs, but displays them one by one in a vertical scrolling fashion?
In that case, we won’t be passing props to style the BlogsContainer component, but rather have another Banner component.
Well, it depends. Is the project you are working on using Class-based components? If yes, it’s pretty likely you would need to use HOCs, since hooks are not available for them. Otherwise, I do consider that hooks are still a better approach to the issue we have had an overview of.
Let’s see how hooks would solve our issue:
First, we would have to create an useBlogs
hooks to handle the retrieval of the blogs data:
import { useState, useEffect } from 'react';
const useBlogs = (getBlogs) => {
const [blogs, setBlogs] = useState([]);
useEffect(() => {
setBlogs(getBlogs());
}, []);
return [blogs, setBlogs];
};
export default useBlogs;
And then we’ll have to update the components’ definitions in the Home
view:
import React from 'react'
import { getArchivedBlogs, getPopularBlogs, getRecentBlogs } from '../api';
import BlogsContainer from '../components/BlogsContainer';
import useBlogs from '../hooks/useBlogs';
const RecentBlogs = (props) => {
const [blogs] = useBlogs(getRecentBlogs);
return <BlogsContainer blogs={blogs} {...props} />
};
const PopularBlogs = (props) => {
const [blogs] = useBlogs(getPopularBlogs);
return <BlogsContainer blogs={blogs} {...props} />
};
const ArchivedBlogs = (props) => {
const [blogs] = useBlogs(getArchivedBlogs);
return <BlogsContainer blogs={blogs} {...props} />
};
const Home = () => {
return (
<div className='home'>
<section>
<h2>Recent Blogs</h2>
<RecentBlogs />
</section>
<section>
<h2>Popular Blogs</h2>
<PopularBlogs />
</section>
<section>
<h2>Archived Blogs</h2>
<ArchivedBlogs />
</section>
</div>
);
};
export default Home;
Through the hooks approach, we can benefit from handling the compositional aspect of React much more elegantly, not having to define a boilerplate HOC, but rather a nicer hook, and use that as needed, without having to go through multiple component factories just to get various props.
It also allows us greater control over the state, which is a huge plus in case we need to do some more extensive computation on the blog state of each component, like filtering or sorting through the blogs.
I do believe that HOCs are extremely useful to understand, but if you are not actively working with Class components, I strongly suggest you would look into hooks, as they are simpler to work with overall.
I hope you have learned something new by reading this article, and hope you have enjoyed it. Catch you on the next one!
Cheers!
If you have enjoyed reading this article, you can also support me by buying me a coffee here.
Also published here