ReactJS - Higher-Order Components



Since react components are interconnected by composition (having one component inside another) rather than by inheritance, logic used in a react component will not be shared to an another component directly. React community provides multiple option to share the logic between components and one such option is Higher Order Component. HOC is not react api, per se, but a design pattern with no side effects.

Let us learn how to use Higher Order Component in this chapter.

How to use Higher Order Component

Basically, HOC is a function that takes a react component as input and then create a new react component based on the input component and return the newly created (wrapped) component. For example, HOC function may receive a pure data rendering component as input, then return a new component, which will have data fetching capabilities & data rendering capabilities using input component.

Let us see how to use HOC and share logic between two component in the step by step manner. Let us consider the scenario of fetching and rendering the data from an external URL.

  • Create a HOC function with one or more input parameters depending on the functionality.

  • The first parameter to the HOC function should be a react component with secondary logic (For example, data rendering logic).

  • The second parameter to the HOC function should be defined as per our requirement. For our data fetching scenario, the data url is the necessary information for fetching the data. So, we should include it as second parameter of our HOC function.

function createHOC(WrappedComponent, url) {
   // create new component using WrappedComponent
}
  • Any number of parameter is fine for HOC function, if it is indeed necessary.

  • Create a new component inside the HOC function supporting primary logic (For example, data fetching logic using second url parameter) in it's componentDidMount event.

function createFetchHOC(WrappedComponent, url) {
   class DataFetcher extends React.Component {
      componentDidMount() {
         fetch(url)
            .then((response) => response.json())
            .then((data) => {
               this.setState({
                  data: data
               });
         });
      }
   }
}
  • Render the input component by passing the data fetched from componentDidMount event.

function createFetchHOC(WrappedComponent, url) {
   class DataFetcher extends React.Component {
      render() {
         return (
            <WrappedComponent data={this.state.data} {...this.props} />
         )
      }
   }
}
  • Return the newly created component.

function createFetchHOC(WrappedComponent, url) {
   class DataFetcher extends React.Component {
   }
   return DataFetcher;
}
  • Create a new component by combining DataFetcher (createFetchHOC) and Wrapped component.

const UserListWithFetch = createFetchHOC(
   UserList,
   "users.json"
);
  • Finally, use the new component in any place as you wish

<UserListWithFetch />

Applying HOC component

Let us create a new application by applying the HOC component.

First of all, create a new react application and start it using below command.

create-react-app myapp
cd myapp
npm start

Next, open App.css (src/App.css) and remove all CSS classes.

// remove all css classes

Next, create a new HOC function as shown below −

import React from 'react';
function createFetchHOC(WrappedComponent, url) {
   class DataFetcher extends React.Component {
      constructor(props) {
         super(props);
         this.state = {
            data: []
         };
      }
      
      componentDidMount() {
         fetch(url)
         .then((response) => response.json())
         .then((data) => {
            this.setState({
               data: data
            });
         });
      }
      render() {
         return (
            <WrappedComponent data={this.state.data} {...this.props} />
         )
      }
   }
   return DataFetcher;
}
export default createFetchHOC;

Next, create a file, users.json (public/users.json) in the public folder to store user information. We will try to fetch it using FetchRenderProps component and show it in our application.

[{"id":1,"name":"Fowler","age":18},
{"id":2,"name":"Donnell","age":24},
{"id":3,"name":"Pall","age":26}]

Next, create a file, todo_list.json (public/todo_list.json) in the public folder to store todo list information. We will try to fetch it using FetchRenderProps component and show it in our application.

[{"id":1,"title":"Learn JavaScript","is_done":true},
{"id":2,"title":"Learn React","is_done":true},
{"id":3,"title":"Learn Typescript","is_done":false}]

Next, Create a new component, UserList (src/Components/UserList.js) to render users as shown below −

import React from "react";
class UserList extends React.Component {
   constructor(props) {
      super(props);
   }
   render() {
      return (
         <>
            <ul>
               {this.props.data && this.props.data.length && this.props.data.map((item) =>
                  <li key={item.id}>{item.name}</li>
               )}
            </ul>
         </>
      )
   }
}
export default UserList;

Here, we have used the data props to render the user list

Next, create a new component, TodoList (src/Components/TodoList.js) to render todos as shown below −

import React from "react";
class TodoList extends React.Component {
   constructor(props) {
      super(props);
      this.todos = this.props.data
   }
   render() {
      return (
         <>
            <ul>
               {this.props.data && this.props.data.length && this.props.data.map(
                  (item) =>
                  <li key={item.id}>{item.title} {item.is_done && <strong>Done</strong>}</li>
               )}
            </ul>
         </>
      )
   }
}
export default TodoList;

Here, we have used the data props to render the todo list.

Next, create a new component, SimpleHOC to render both user list and todo list through single HOC component.

import React from "react";
import UserList from "./UserList";
import TodoList from "./TodoList";
import createFetchHOC from "./createFetchHOC";
const UserListWithFetch = createFetchHOC(
   UserList,
   "users.json"
);

const TodoListWithFetch = createFetchHOC(
   TodoList,
   "todo_list.json"
);

class SimpleHOC extends React.Component {
   constructor(props) {
      super(props);
   }
   render() {
      return (
      <>
         <UserListWithFetch />
         <TodoListWithFetch />
      </>
      )
   }
}
export default SimpleHOC;

Here we have,

  • Created UserListWithFetch component by combining TodoList and DataFetcher component.

  • Created TodoListWithFetch component by combining Users and DataFetcher component.

Next, open App.js and update it with SimpleHOC component.

import './App.css'
import React from 'react';
import SimpleHOC from './Components/SimpleHOC'

function App() {
   return (
      <div className="container">
         <div style={{ padding: "10px" }}>
            <div>
               <SimpleHOC />
            </div>
         </div>
      </div>
   );
}
export default App;

Finally, open the application in the browser and check the final result. The application will render as shown below −

Applying HOC Component

Summary

Higher Order Component is an efficient method to share logic between components. It is extensively used in many third party component with good success rate and it is time-tested method to share logic in the react domain.

Advertisements