Higher-Order Components in React
If you have been using React for a long time, you might have felt that we need to have some copies of the same logic in multiple components. Following are a few used cases :
- Infinite scrolling in different views, all having additional data.
- Data used by members from subscription.
- App components that need user logged-in user data.
- Displaying multiple lists (e.g., Users, Locations) with a search feature.
- Enhancement of different card component views with the same border and shadow.
Now you might be wondering if there is any way in React to share the logic across multiple components without rewriting it.
Well, yes! There is an advanced technique to handle such things, which I mentioned earlier, called Higher-Order Components (HOCs).
In this blog, we will understand HOCs in detail, like when to use this, and will see different use cases with real-life examples.
What are Higher-Order Components?
A higher-order component is a function we can say that takes one or more components as an argument and returns a new and enhanced or upgraded component.
As shown in the above example, we have a higher-order component, a function taking a piece called <WrappedComponent /> as an argument. We also have a new feature called HOC which returns <WrappedComponent /> from its render function. Although it adds no functionality, it illustrates the typical pattern that every HOC function follows.
We can call the HOC as shown below:
A higher-order component transforms one component into another piece. However, it does not modify the input component. Instead, a HOC component creates the original element by wrapping it into a container component.
Coding A Practical HOC
Suppose we want to create a list of products with search functionality. First, we will have to store our product array in a file and import it as a separate component as follows:
CREATING OUR FIRST COMPONENT
We have created our first component, `ProductCard.` This component takes care of handling the presentation of the data. The product data will be received via props and will pass down each to the `ProductCardetailent.
RENDERING PRODUCT LIST
Now, we will create another component that will iterate over product data using the ‘.map()` function. First, let’s invoke this component, `ProductList.`
This component will render data in the `ProductCard` detail.
PRODUCT LIST WITH SEARCH FEATURE
We want the users to be able to search for any items using the input field. The state of the search should determine the list of items displayed on the screen. This is a stateful component input of a user stored in a state value called `searchTerm`. Let’s invoke this `ProductListsWithSearch`.
The `searchTerm` is given an empty string of a state. Then, whatever value entered by the user in the search box is obtained and is used to set the new form for `searchTerm`.
Now, if we want to render a list of users with the same search functionality.
Similarly, we will create a component mentioned above with the name `UserListWithSearch`.
CONVERTING ProductListWithSearch INTO HOC
You might have noticed that we will have to repeat the search logic in both components. This is where Higher-Order Component comes into the picture. First, I will create a HOC with the name `withSearch.`
As shown in the above code, firstly, we have imported the higher-order component, which we have created with the name `withSearch.` Then we added the filter method to filter the data based on what the user entered in the search input. Then, we wrapped it with the `withSearch` component.
That’s all! We have made a fully functional HOC.
Limitations Of Higher-Order Components
1. Do not use HOC inside the render method
As shown above, if we use the HOC inside the render method, then we cannot save the identity of the HOC across the render, which will cause the application’s overall performance.
But the problem here is not just performance. Remounting the component causes the element’s state and all its child components to be lost.
Instead, we should write HOC outside the component, i.e., outside the render method, so that the resulting feature will be created only once.
2. The static method must be copied over
Using the static method on a component is not applicable, especially if you want to use HOC. When you use HOC on an element, it returns new enhanced detail. This is because the new member has no static method of the original part.
To solve this problem, you can use hoist-non-react-statics to copy non-static React methods as shown below automatically:
3. The refs are not passed through
We want to pass props to a wrapped component. We need to remember refs since they are not given through. It is not a prop, but React handles it.
The solution is to use the API `React.forwardRef `.
To conclude, a higher-order component is a function that returns a new component. It also can pass the higher-order components into the other higher-order components since they take components as input and return the other components as output. However, this can bring unnecessary nesting into the component.
Libraries such as Redux and React Router are good examples of why we should consider implementing higher-order components.
We use higher-order components to reuse the logic in React apps mainly. However, HOCs are inconvenient when we want to share non-visual reasons. React hooks are a perfect mechanism for code reusability.