In the ever-evolving realm of web development, mastering advanced concepts is essential for creating robust and flexible applications. In this article, we'll delve into one of these foundational React concepts: Higher-Order Components, commonly known as HOCs.
Whether you're a seasoned developer seeking advanced techniques or a curious beginner eager to learn, we'll walk through the creation and application of HOCs step by step. Get ready to enrich your development toolkit with this powerful approach while discovering how to enhance the readability, reusability, and maintainability of your code.
What is a Higher Order Component?
Imagine that you own a pastry shop and you specialize in creating a wide variety of pastries. In your pastry shop, you have croissants, muffins, and danishes. Each pastry must have a label indicating its type and ingredients. If you were to do this for every pastry manually, it would require a significant amount of time. Instead of doing it manually, you can use a "robot" that will do it automatically for you.
This robot is a Higher-Order Component (HOC), a function that takes a component (your pastry) and returns your pastry with additional functionalities or features (in this case, a label indicating its type and ingredients).
Advantages
Higher-Order Components (HOCs) offer several significant advantages that enhance how we develop our React applications. Here are some of these advantages, accompanied by concrete examples:
- Reusability: One major strength of HOCs is their ability to be reused in different parts of your application. Since an HOC is a function with specific logic, you can customize it to suit your needs while keeping it flexible. For instance, an authentication HOC could be adapted to handle access to different types of content.
- Separation of Concerns: Instead of cluttering a component with multiple distinct logics, HOCs allow for the separation of these responsibilities into distinct components. You can easily envision creating a separate HOC for each logic, making your components clearer and better organized.
- Code Modularity: HOCs add a modular layer to your code. You can add or remove an HOC without affecting the functionality of the underlying component, making maintenance and adaptation of your application easier. In essence, HOCs nest without altering overall stability.
- Facilitation of Unit Testing: Due to their isolated nature, HOCs simplify unit testing. You can test the specific logic of an HOC without the complexity of other parts of the application.
By combining these advantages, you'll be able to create more flexible and maintainable applications while minimizing code duplication.
Practical Case
The problem
Let's take a concrete example to illustrate the relevance of Higher-Order Components. Suppose we're developing an application with an authentication system. In this application, different parts of the interface require authentication:
- Home Page: This page doesn't require authentication.
- List of Books: This list can be viewed without authentication.
- Top Books: This section is reserved for authenticated users.
- User Ratings: Similarly, only logged-in users can view the ratings.
Imagine we have a component that imports all the others:
export const App = () => {
return (
<div>
<Home />
<BooksList />
<TopBook />
<UsersRating />
</div>
)
}
Now, let's consider a component for each part:
export const Home = () => {
return (
<div>
<h1>Hey !</h1>
<p>This is a welcome message !</p>
</div>
)
}
export const BooksList = () => {
return (
<div>
<p>Books list</p>
</div>
)
}
export const TopBook = () => {
const isConnected = false
if(!isConnected) {
return (
<p>You don't have access !</p>
)
}
return (
<div>
<p>Top Book</p>
</div>
)
}
export const UsersRating = () => {
const isConnected = false
if(!isConnected) {
return (
<p>You don't have access !</p>
)
}
return (
<div>
<p>UsersRating</p>
</div>
)
}
We notice that the two components with the restriction logic repeat the same code. While this isn't a big issue with only two components, imagine having 10 components - that's a lot of code repetition. Given this scenario, it's important to find a solution that handles conditional content display based on authentication status.
This is where Higher-Order Components (HOCs) come into play.
Solution : Higher Order Components (HOCs)
To address this issue of code repetition in multiple places, let's isolate the authentication logic in an HOC called withAuthentication
:
export const withAuthentication = (Component) => {
const isConnected = false
if (!isConnected) {
return <p>You don't have access !</p>
}
return (props) => <Component {...props} />
}
Next, we'll apply this HOC to the components that require access restriction, like this:
export const TopBook = withAuthentication(() => {
return (
<div>
<p>Top Book</p>
</div>
)
})
export const UsersRating = withAuthentication(() => {
return (
<div>
<p>UsersRating</p>
</div>
)
})
Assuming you retrieve isConnected from either props or context, you now have isolated and reusable logic.
Going further
We can go further with HOCs. Let's imagine now that we want to do some layout work with a profile view:
export const Profile = () => {
// This is the beta version of the Profile
return (
<div>
<p>Beta Profile</p>
<h1>Nicolas BROUARD</h1>
</div>
)
}
We have our Profile view and we want to add a "Beta" banner to indicate that this is indeed the Beta version of the profile. We can also use an HOC:
export const withBetaBanner = (Component) => {
return (props) => (
<>
<header className="beta-header">This is a beta version</header>
<Component {...props} />
</>
)
}
You can easily nest various HOCs if needed:
const ProtectedBetaProfile = withAuthentication(withBetaBanner(Profile))
return <ProtectedBetaProfile />;
This allows you to isolate different rules, making unit testing easier! You can also manipulate props if necessary. HOCs are very powerful!
You can also manipulate props if necessary. HOCs are very powerful!
Conclusion
In conclusion, you now have all the tools you need to fully leverage the power of Higher-Order Components (HOCs) in your React projects.
By combining concepts such as prop forwarding, HOC composition, and using TypeScript for type safety, you can create more flexible, cleaner, and efficient components. HOCs provide a practical approach to handling cross-cutting concerns and simplifying your components, which can greatly facilitate maintenance and expansion of your applications. Keep experimenting with HOCs in your projects and don't hesitate to explore concrete examples to deepen your understanding.
Mastering the art of HOCs will give you a significant advantage in building robust and scalable React applications. So, dive in, apply this knowledge, and transform your components into flexible and powerful building blocks for your future React creations.
If you enjoyed this tutorial, please consider following me for more helpful content. Your support is greatly appreciated! Thank you!
Top comments (18)
HOCs are super helpful!
Seems you forgot to remove:
'if (!isConnected) {
return
You don't have access !
}'
from your components after implementing hocs
Thanks for your feedback,
This is my very first article and I left a small typo thanks!
Congrats for your 1st article Nick!
Thank's a lot ! :)
Nice example.
The above example can be implemented using a common wrapper component as well.
It will be super helpful if we can see some more examples for HOC where
using HOC is the only way and cannot be implemented using wrapper components.
Nice article, thank you.
Great explanation! 👏🏼
Nice article. I didn't even know HOCs existed until now!
it's great
That was a great article, I was looking for a introduction to React's Higher-Order Components. It will be helpful
Hi ! Thank's bro !
congratulations for yur first article, he is very nice, quadruple monstre,
spike down
easy explanation, Thanks
Congrats on your first article. It's straightforward.
As Nickap mentioned there are some small typos.
After adding HOC you should remove these lines from your components:
Additionally the HOC needs a slightly change: