Higher Order Component In React

Introduction

 
In the previous article, we learned about Portals and Error Boundary along with their usage in ReactJS. In this article, we will learn about Higher-Order Components (HOCs) and their usage in React.
 

What is HOC?

 
HOC is not a feature in React or any other programming language. It is just a concept that involves the compositional nature of React. Higher-Order Components are not part of React API either. A component transforms Props into UI while on the other hand, Higher-Order Component converts a component into another component
 
The concept of Higher-Order Components is mainly used for reusability and for the decomposition of components into simple and smaller functions that can be reused. This makes a function capable of doing a single task making the whole application easy to debug and maintenance.
 
In Haskell language, the inspired pseudo-code has the below signature.
  1. hocFactory:: W: React.Component => E: React.Component   
Where W represents WrappedComponent which is a React component being wrapped and E represents Enhanced Component which returns a new component.
 
Basically, HOC involves manipulating WrappedComponents which can be done in 2 ways.
  1. Props Proxy
  2. Inheritance Inversion

Props Proxy

 
In Props Proxy, as the name suggests, it passes properties received from Higher-Order Component.
 
Syntax
  1. function ppHOC(WrappedComponent) {  
  2.    returnclass PP extends React.Component {  
  3.       render() {  
  4.          return <WrappedComponent {...this.props}/>  
  5.       }  
  6.    }  
  7. }  
As per the syntax above, the Props Proxy returns the React element which is of type WrappedComponent.
 
There are many ways to represent Props Proxy.
  1. Manipulating Props
  2. Abstracting State
  3. Accessing the instance via Refs
  4. Wrapping of WrappedComponent with other elements

Inheritance Inversion

 
This is quite different from Props Proxy. It allows HOC to access WrappedComponents instance using this keyword. This lets us access props, state, and render methods.
 
Syntax
  1. function iiHOC(WrappedComponent) {  
  2.    return class Enhancer extends WrappedComponent {  
  3.       render() {  
  4.          return super.render()  
  5.          }  
  6.       }  
  7. }  
So, we can see that it has reversed the effect of Props Proxy; thus the name suggests Inheritance Inversion as it is extending the WrappedComponent. Inversion Inheritance can be used for -
  1. Conditional Rendering
  2. State Manipulation  

Demo of Higher-Order Components in React

 
Let us create a small demo to display a list for companies and Employees in a table. In this demo, we will create a component as TableRowComponent to be used in CompanyComponent and EmployeeComponent to just pass data to TableRowComponent.
 
For the above implementation, you need to first create the TableRowComponent.js file in which, based on the input, a complete row is created.
  1. import React, { Component } from 'react'    
  2.     
  3. class TableRowComponent extends Component {    
  4.     render() {    
  5.         return (    
  6.             <tr>    
  7.                 <td>    
  8.                     {this.props.obj.Id}    
  9.                 </td>    
  10.                 <td>    
  11.                     {this.props.obj.Name}    
  12.                 </td>    
  13.                 <td>    
  14.                     {this.props.obj.ContactNumber}    
  15.                 </td>    
  16.             </tr>    
  17.         )    
  18.     }    
  19. }    
  20.     
  21. export default TableRowComponent   
Now, we will create the EmployeeList.Js file in which the above TableRowComponent is used.
  1. import React, { Component } from 'react'    
  2. import TableRowComponent from './TableRowComponent'    
  3.     
  4. export class EmployeeList extends Component {    
  5.     constructor(props) {    
  6.         super(props)    
  7.         
  8.         this.state = {    
  9.              Employees:[    
  10.                  {    
  11.                      Id : 1,    
  12.                      Name : 'ABC',    
  13.                      ContactNumber: '1123456789'    
  14.                  },    
  15.                  {    
  16.                     Id : 2,    
  17.                     Name : 'XYZ',    
  18.                     ContactNumber: '09873643775'    
  19.                 },    
  20.                 {    
  21.                     Id : 3,    
  22.                     Name : 'MNO',    
  23.                     ContactNumber: '29354546675'    
  24.                 }    
  25.              ]    
  26.         }    
  27.     }    
  28.         
  29.     tableRow(){    
  30.         if(this.state.Employees instanceof Array){    
  31.             return this.state.Employees.map(function(object,i){    
  32.                 return <TableRowComponent obj={object} key={i}></TableRowComponent>    
  33.             })    
  34.         }    
  35.     }    
  36.     render() {    
  37.         return (    
  38.             <div className="container">    
  39.                 <table>    
  40.                     <thead>    
  41.                         <tr>    
  42.                             <td>Employee Id</td>    
  43.                             <td>Employee Name</td>    
  44.                             <td>Contact Number</td>    
  45.                         </tr>    
  46.                     </thead>    
  47.                     <tbody>    
  48.                         {this.tableRow()}    
  49.                     </tbody>    
  50.                 </table>    
  51.             </div>    
  52.         )    
  53.     }    
  54. }    
  55.     
  56. export default EmployeeList 
Now, also create a CompanyList.js file.
  1. import React, { Component } from 'react'    
  2. import TableRowComponent from './TableRowComponent'    
  3.     
  4. export class CompanyList extends Component {    
  5.     constructor(props) {    
  6.         super(props)    
  7.         
  8.         this.state = {    
  9.              Companies:[    
  10.                  {    
  11.                      Id :1,    
  12.                      Name : 'Wipro',    
  13.                      ContactNumber:'3567575'    
  14.                  },    
  15.                  {    
  16.                     Id :2,    
  17.                     Name : 'Google',    
  18.                     ContactNumber:'3983945'    
  19.                 },    
  20.                 {    
  21.                     Id :3,    
  22.                     Name : 'Facebook',    
  23.                     ContactNumber:'475686787'    
  24.                 }    
  25.              ]    
  26.         }    
  27.     }    
  28.         
  29.     tableRow(){    
  30.         if(this.state.Companies instanceof Array){    
  31.             return this.state.Companies.map(function(object,i){    
  32.                 return <TableRowComponent obj={object} key={i}></TableRowComponent>    
  33.             })    
  34.         }    
  35.     }    
  36.     
  37.     render() {    
  38.         return (    
  39.             <div className="container">    
  40.             <table>    
  41.                 <thead>    
  42.                     <tr>    
  43.                         <td>Company Id</td>    
  44.                         <td>Company Name</td>    
  45.                         <td>Contact Number</td>    
  46.                     </tr>    
  47.                 </thead>    
  48.                 <tbody>    
  49.                     {this.tableRow()}    
  50.                 </tbody>    
  51.             </table>    
  52.         </div>    
  53.         )    
  54.     }    
  55. }    
  56.     
  57. export default CompanyList    
Now, add both the newly added CompanyList and EmployeeList in the App.js file.
  1. import React from 'react';      
  2. import './App.css';    
  3. import './css/custom.css';    
  4. import ErrorBoundary from './components/ErrorBoundary'    
  5. import CompanyList from './components/CompanyList'    
  6. import EmployeeList from './components/EmployeeList';    
  7.     
  8. function App() {    
  9.   return (        
  10.     <div>    
  11.       <h1>Company List</h1>    
  12.       <CompanyList></CompanyList>    
  13.       <h1> Employee List</h1>    
  14.       <EmployeeList></EmployeeList>        
  15.     </div>    
  16.   );    
  17. }    
  18.     
  19. export default App;   
The output of the above program will be displayed as below.
 
Higher Order Component In React
In the above example, we have reviewed how Higher-Order Components work.
 
For Higher-Order Components, we need to remember 2 things as mentioned below.
  • These components take input as component and
  • Return a new component. 
Higher-Order Components are commonly used in third-party libraries, like Redux or React Router.
 
In short, HOC helps you organize React code in a better way and to reduce the code redundancy. Even third-party libraries like Redux or React Router use this concept.
 

Advantages of Higher-Order Components  

  1. Used when there is a need to bundle up a repeating pattern.
  2. Make debugging easier 
  3. Pass through props which are unrelated to HOC.

Disadvantages of Higher-Order Components 

  1. No need to overuse, other approaches can also be used
  2. It Mutates the original component.
  3. Never use Higher Order Component inside render() method.

Use Cases For Higher-Order Components

  1. Loader to create a Higher-Order Component that can be used whenever required.
  2. Whenever needed to manage common user interaction.
  3. When a user needs to define some specific styles.
  4. You can create your Dialog as a component and can be used in the entire application.

Summary

 
In this article, we have learned about the concepts and usage of Higher-Order Components. In the next article, we will go in detail about the concept of Render Props and Context in React.js.