Back

The Transition From Higher-Order Component Pattern to React Hooks Pattern

The Transition From Higher-Order Component Pattern to React Hooks Pattern

Reusability is an essential principle to follow in software development and programming. React(also known as React.js or ReactJS) is a component-based declarative user interface library that provides us with a solid ground to adhere to it. React’s Higher-Order Component(HOC) Pattern is a great way to achieve it.

In this article, we will learn about the Higher-Order Component pattern and then transition to understanding the React Hooks pattern with lots of examples. Please read this article if you are new to React or look forward to understanding how the Container Component Pattern works.

The Higher-Order Component(HOC) Pattern

The Higher-Order Component is a pattern to extend and reuse an existing component. You do not want to duplicate code logic across components. It is extra work and causes adverse situations like bugs maintenance issues. Also, you may want to enhance a component and use it for a completely different purpose.

As defined in React’s official documentation,

A higher-order component is a function that takes a component and returns a new component.

So, you can visualize the usage of HOC as,

const EnhancedComponent = HOC(WrappedComponent);

Here the HOC is the higher-order component that takes WrappedComponent and returns a new EnhancedComponent. Now let’s learn more about HOC with examples.

Let’s consider a couple of React components to list down movies from a database and show analytics about those movies, respectively.

  • MovieListWithHOC: A component that lists down the movie information like name, cover image, director name, and so on.
  • MovieAnalyticsWithHOC: A component that shows the analytics on the movie data.

These components have a standard requirement, i.e., fetching the data from the database. Let’s assume we have an API(URI: https://json-faker.onrender.com/movies) that we can request to fetch the movie data as a response.

The logic of fetching the movie data using the API is a common need for both components. But wait, we don’t want to repeat the data fetching logic in both components. Instead, we will use the HOC pattern to reuse the fetching logic and enhance the components.

First, let us create a HOC with the movie data fetching logic.

import React from "react";

// Craete a HOC that takes a component as an argument
const withFetch = (WrappedComponent) => {
    
  // Create a Class Component that will
  // enhance the WrappedComponent 
  class WithFetch extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        movies: [],
      };
    }

    componentDidMount() {
      // Fetch the movie data by making an API call  
      fetch("https://json-faker.onrender.com/movies")
        .then((response) => response.json())
        .then((data) => {
          this.setState({ movies: data.movies });
        });
    }

    render() {
      return (
        <>
          {this.state.movies.length > 0 && (
            <WrappedComponent movies={this.state.movies} />
          )}
        </>
      );
    }
  };
  
  // It is optional to set the display name of the
  // component but useful when comes to debug the HOC.
  WithFetch.displayName = `WithFetch(${WithFetch.name})`;
  
  // Finally, return the component
  return WithFetch;
};

export default withFetch;

A few things to note here:

  • We have created a Rect component(A regular JavaScript function) withFetch that takes a component(WrappedComponent) as an argument.
  • The withFetch component returns a component(Class component).
  • We create a state variable called movies and update it using the response of the API call in the componentDidMount lifecycle method.
  • Now take a look into the JSX part of the component. Here we enhance the WrappedComponent passing the movies data as props.
  • Finally, we return the component(Class Component). The core part of this component is the WrappedComponent enhanced with the movie data.

It is powerful because now you can pass a presentational component(people call it a dumb component!) to the withFetch Higher-Order component to get back a fully operational component with data. The best part is several presentational components(say, one to list movies and another to show analytics), which can use the same HOC to fetch data and represent them differently. Isn’t that amazing?

Let’s use the HOC to create a movie listing component,

import MovieContainer from "../movies/MovieContainer";
import "../movies/movies.css";
import withFetch from "./MovieWrapper";

const MovieListWithHOC = (props) => {
  const { movies } = props;
  return (
    <div className="movie-container">
      <h2>Movie List - With HoC</h2>
      <MovieContainer data={movies} />
    </div>
  );
};

export default withFetch(MovieListWithHOC);

As the code above shows, the MovieListWithHOC is a presentational component enhanced with the HOC(withFetch).

Please note the export, it says,

export default withFetch(MovieListWithHOC);

So, the MovieListWithHOC component gets the movie data as props and pass it to another component called MovieContainer to render the data. You can take a look into an example implementation of the MovieContainer component from here.

Similarly, we can have another component for showing movie analytics using the same movie data,

import MovieAnalytics from "../movies/MovieAnalytics";
import "../movies/movies.css";
import withFetch from "./MovieWrapper";

const MovieAnalyticsWithHOC = (props) => {
  const { movies } = props;
  return (
    <div className="movie-container">
      <h2>Movie Analytics - With HoC</h2>
      <MovieAnalytics data={movies} />
    </div>
  );
};

export default withFetch(MovieAnalyticsWithHOC);

You can take a look into an example implementation of the MovieAnalytics component from here.

I hope you found the usage of HOC impressive. It is an excellent use of code reusability. Many React-based libraries (including React) like Redux, React-Router use this pattern.

Before we move to the next section to understand the React Hook patterns, please be aware of a few Caveats mentioned in the React documentation.

Open Source Session Replay

OpenReplay is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data.

replayer.png

Start enjoying your debugging experience - start using OpenReplay for free.

The React Hooks pattern

React Hooks are a new inclusion to the library in version 16.8. Before that, we couldn’t use the state and a few other React features outside the class-based components. In React, we can create a component using JavaScript classes or functions. With the advent of Hooks, now it is much easier to embrace functional programming in React.

With functional components and hooks together, we can separate the concern of managing state and side-effects from a component to a hook. The hook can isolate all these components’ plumbing works to keep it pure.

React provides various standard hooks out-of-the-box. The useState, useEffect, useMemo, useCallback, useRef, are a few to name. We can also create custom hooks to separate the reusable logic and possible side-effects from a component. If you are new to React Hooks, here is a short video to give you the fundamentals of React Hooks.

Alright, we have learned that HOC helps in reusability, and so does the React Hook. So can we convert a HOC to a React Hook and use that instead? That way, we will be fully “functional”, right? Yes, let’s do that.

It is a convention to name a React Hook starting with use(yes, the letter u is in the smaller case!). Let us create a custom hook called useFetch.

import { useState, useEffect } from "react";

const useFetch = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch(url);
      const json = await response.json();
      setData(json);
      setLoading(false);
    };
    fetchData();
  }, [url]);

  return [loading, data];
};

export { useFetch };

Let’s understand the code above step-by-step:

The useFetch hook is a JavaScript function that takes an URL as an argument. It is the API URL to fetch data. The hook is very flexible to fetch data using any API URL passed. We will pass the API URL to fetch the movie data in our case.

The hook uses two state variables, loading and data. The state variables are declared and tracked for changes using the useState hook.

useState: It helps manage the local state so that React can preserve and use the state between re-renders. The useState hook returns the current state value and a function to update the state value. We can also pass the initial state value to the useState() hook.

Next, the useEffect hook manages the side-effects of making the API call and fetching the data. Once complete, we update the state variables indicating the data loading is done and fetched.

useEffect: The useEffect hook helps in performing side-effects. The side-effect can be anything responsible for changing the state of your application. A typical example of a side-effect is making an API call and changing the local state to preserve the response data.

Finally, we return both the state variables in an array.

Great, not it’s the time to use the hook in a component to get the movie data and render in the UI. How about we use it for the same MovieAnalytics component we have used before?

Let’s create a component called MovieAnalyticsWithHook to demonstrate it,

import { useFetch } from "../hooks/useFetch";
import MovieAnalytics from "../movies/MovieAnalytics";

const MovieAnalyticsWithHook = () => {
  const MOVIE_URI = "https://json-faker.onrender.com/movies";
  const [loading, data] = useFetch(MOVIE_URI);
  return (
    <div>
      <h1>Movie Analytics with Hooks</h1>
      {
          loading ? 
          <h2>Loading...</h2> : 
          <MovieAnalytics data={data.movies} />
      }
    </div>
  );
};

export default MovieAnalyticsWithHook;

We call the useFetch hook passing the movie API URI in the code above. It returns the loading and data values that we destructure into two variables.

const [loading, data] = useFetch(MOVIE_URI);

Next, in the JSX part, we use loading and data to show the movie analytics.

That’s it. I hope you enjoyed learning about the Reach Hooks as well. You can find all the source code used in this article from here,

The HOC and Hooks Project on GitHub

In Summary

To Summarize,

  • The Higher-Order Component(HOC) Pattern is a popular React pattern to empower reusability among components.
  • HOC is a function that takes a component as an argument and returns an enhanced version.
  • HOC helps to isolate logic and state management in a separate class-based component.
  • With React Hooks, state management can occur outside of a class.
  • Hooks empower developers to use the functional programming aspects in React.
  • There are standard hooks, and we can write custom hooks.
  • Hooks are helpful to isolate the state and side-effects from a component and encourage reusability.
  • The React developer community aggressively adopts React Hooks over Higher-Order Components for a new implementation.

Before we end…

I hope you found this article insightful and informative. After spending many years with ReactJS, I have started a YouTube Video Series that aims to cover all the aspects of React.js end to end. Please SUBSCRIBE if you find it helpful.

Let’s connect. I share my learnings on JavaScript, Web Development, and Blogging on these platforms as well: