The Higher-Order Components(HOC) pattern is way to reuse component logic and enhance component functionality. In React, it provides a way to build a component which can take another component, enhance its functionality and return the enhanced component. In this post, we will be reviewing this pattern and explaining it with a real life use case which comes for most of the developers while implementing a react component. You can download the code we worked in below blog post from navinprasadofficial/hoc-example (github.com)
Creating a HOC
Let us start with an use case, display loading…(or a loading indicator) on screen while your component is fetching data. This is most common thing we do in any react component which fetch data but instead of adding loading indicator and fetching data code on each component , we will be building HOC which will be responsible for this. Here are step by step instruction for following the tutorial
Create a new React Project with typescript and create a new folder “components” in src folder.
Create University.tsx file in Components folder and add below code in it.
function University(props: any) {
const { data } = props;
return (
<div>
<h1>Want to Study, Here are List of University</h1>
{data?.map((item: any) => (
<table>
<th>
<td>University Name</td>
<td>Country</td>
<td>Web Page</td>
</th>
<tr>
<td>{item.name}</td>
<td>{item.country}</td>
<a href={item.web_pages[0]}>{item.web_pages[0]}</a>
</tr>
</table>
))}
</div>
);
}
export default University;
The above code is a React functional component called “University” that renders a list of universities. It takes a “data” prop, which is expected to be an array of objects containing information about each university. The component displays the university name, country, and a hyperlink to their web page.
Inside the component, a heading is displayed, indicating the purpose of the list. The “data” array is then mapped over to generate a table structure for each university item. The table includes header cells for “University Name,” “Country,” and “Web Page.” The university information is displayed in table rows, with the name and country in separate cells and the web page as a clickable link.
By exporting the “University” component as the default export, it can be imported and used in other parts of a React application.
Create Bored.tsx file in components folder and add below code in it.
function Bored(props: any) {
const { data } = props;
return (
<div>
<h2>Feeling Bored, you can try below activity</h2>
{data ? (
<>
<table>
<th>
<td>Activity</td>
<td>Type</td>
<td>Participants</td>
</th>
<tr>
<td>{data.activity}</td>
<td>{data.type}</td>
<td>{data.participants}</td>
</tr>
</table>
</>
) : (
<></>
)}
</div>
);
}
export default Bored;
The above code is a React functional component named “Bored” that displays a single activity to try when feeling bored. It takes a “data” prop, which is expected to be an object containing information about the activity.
Inside the component, a heading is displayed, indicating the purpose of the component. The “data” object is checked for existence using a conditional statement. If the “data” object is truthy, a table structure is generated to display the activity details. The table includes header cells for “Activity,” “Type,” and “Participants.” The activity information is displayed in a single row, with each detail in separate cells.
By exporting the “Bored” component as the default export, it can be imported and used in other parts of a React application.
In summary, the “Bored” component renders a single activity to try when bored, providing a concise and focused display of the activity information.
Create new file named “withDataLoader.tsx” in components folder and add below code in it This is our hoc component.
import React, { useState, useEffect, ComponentType } from "react";
export interface WithDataLoaderProps<T> {
WrappedComponent: ComponentType<T>;
dataUrl: string;
}
function withDataLoader<T>(props: WithDataLoaderProps<T>) {
const { WrappedComponent, dataUrl } = props;
return function WithDataLoaderWrapper(props: T) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch(dataUrl);
const json = await response.json();
setData(json);
} catch (error) {
setError(error as Error);
} finally {
setLoading(false);
}
};
fetchData();
}, [dataUrl]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<WrappedComponent
{...props}
data={data}
loading={loading}
error={error}
/>
);
};
}
export default withDataLoader;
The above code defines a higher-order component (HOC) named “withDataLoader.” This HOC enhances a component by fetching data from a specified URL and passing it as a prop to the wrapped component.
The “withDataLoader” function takes a generic type parameter “T” and an object “props” of type “WithDataLoaderProps<T>”. The “WithDataLoaderProps<T>” interface specifies two properties: “WrappedComponent,” which represents the component to be wrapped, and “dataUrl,” which specifies the URL from which the data should be fetched.
Inside the function, a new component function is returned, called “WithDataLoaderWrapper,” which takes props of type “T”. This component function sets up state variables using the “useState” hook to manage the data, loading status, and error state.
The effect hook “useEffect” is used to fetch the data from the specified URL when the “dataUrl” changes. It sets the loading state to true, sends an HTTP request to the URL, and updates the data state variable with the received JSON response. If an error occurs during the fetch, it is caught, and the error state is set accordingly. Finally, the loading state is set to false.
The rendered output of the HOC depends on the loading and error states. While the data is being fetched, a “Loading…” message is displayed. If an error occurs, an error message with the specific error is shown. Once the data is successfully fetched, the wrapped component (specified as “WrappedComponent”) is rendered and provided with the fetched data, loading state, and error state as props.
The “withDataLoader” HOC is then exported as the default export, allowing it to be imported and used in other parts of a React application.
In summary, the “withDataLoader” HOC simplifies the process of fetching data from a specified URL and passing it as a prop to a wrapped component. It handles the loading and error states and provides a convenient way to enhance components with data fetching functionality.
Update App.tsx as below
import React from "react";
import logo from "./logo.svg";
import "./App.css";
import withDataLoader from "./components/withDataLoader";
import Bored from "./components/Bored";
import University from "./components/University";
const UniversityWithDataLoader = withDataLoader({
WrappedComponent: University,
dataUrl: "http://universities.hipolabs.com/search?country=India",
});
const BoredWithDataLoader = withDataLoader({
WrappedComponent: Bored,
dataUrl: "http://www.boredapi.com/api/activity",
});
function App() {
return (
<div className="App">
<h1>Higher Order Component Example</h1>
<BoredWithDataLoader />
<UniversityWithDataLoader />
</div>
);
}
export default App;
The above code is a React application that demonstrates the usage of the “withDataLoader” higher-order component (HOC) with two wrapped components: “Bored” and “University.”
In the application’s entry point file, “App.js,” several imports are made, including the React library, a logo image, CSS styles, the “withDataLoader” HOC, and the “Bored” and “University” components.
The “withDataLoader” HOC is used to enhance the “Bored” and “University” components by fetching data from specific URLs and passing it as props to these components. Two instances of the HOC are created: “BoredWithDataLoader” and “UniversityWithDataLoader,” each specifying the wrapped component and the data URL.
The “App” function component is defined, representing the main component of the application. It renders a <div>
element with the class name “App” and includes an <h1>
heading for the title of the application. Inside the <div>
, the “BoredWithDataLoader” component and the “UniversityWithDataLoader” component are rendered.
Finally, the “App” component is exported as the default export of the file, allowing it to be used in other parts of the application.
In summary, this code sets up a React application that demonstrates how to use the “withDataLoader” higher-order component to fetch and pass data to wrapped components. The application renders a title, followed by the “Bored” component with fetched activity data and the “University” component with fetched university data from specific URLs.
Run the project and you can see that you are getting two loading… one for each component and the data after successful fetch of data as below
The provided code demonstrates the implementation of higher-order components (HOCs) in a React application. The “withDataLoader” HOC effectively abstracts the data fetching logic, allowing the “Bored” and “University” components to focus solely on rendering the data. By leveraging the HOC, the code achieves code reusability, separation of concerns, and improved maintainability. The HOC handles loading and error states, ensuring a smooth data fetching experience for the wrapped components. Overall, the code exemplifies the power and benefits of using HOCs to enhance component functionality.
Conclusion
The blog post provides a comprehensive overview of the HOC pattern in React. It explains the concept of HOCs and their significance in achieving code reusability and separation of concerns. The blog post walks through an example code snippet that demonstrates the implementation of the “withDataLoader” HOC, showcasing how it simplifies data fetching and enhances component functionality. By leveraging HOCs, React developers can modularize their code, improve code organization, and easily incorporate common functionalities across multiple components. The blog post serves as a valuable resource for developers looking to deepen their understanding of HOCs and leverage them effectively in their React projects.