visit
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' }
},
)();
You can check out the repo here: 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;
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?
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;
If you have enjoyed reading this article, you can also support me by buying me a coffee .