Article image Higher Order Components (HOC)

24. Higher Order Components (HOC)

Page 68 | Listen in audio

In the world of React, one of the most powerful patterns that developers can leverage is the concept of Higher Order Components (HOCs). A Higher Order Component is essentially a function that takes a component and returns a new component. This pattern is widely used for reusing component logic, and it is a fundamental part of the React ecosystem.

To understand HOCs, let's first delve into the problem they solve. In React, you often find yourself needing to share common functionality across different components. For example, you might have several components that need to fetch data from an API, manage local state, or subscribe to some external data source. Without a way to share logic, you'd end up duplicating code across these components, which is not ideal for maintainability or scalability.

Higher Order Components provide a solution to this problem by allowing you to abstract shared logic into a single place. By wrapping a component with an HOC, you can enhance or modify the behavior of that component without changing its implementation. This promotes code reuse and separation of concerns, making your codebase more manageable and easier to understand.

Let's look at a basic example of an HOC. Imagine you have a component that displays user data, and you want to add a loading spinner while the data is being fetched. Instead of adding the loading logic directly into the component, you can create an HOC that handles the loading state:


function withLoadingSpinner(WrappedComponent) {
  return class extends React.Component {
    render() {
      return this.props.isLoading ? (
        <div>Loading...</div>
      ) : (
        <WrappedComponent {...this.props} />
      );
    }
  };
}

In this example, withLoadingSpinner is an HOC that takes a component, WrappedComponent, and returns a new component that conditionally renders a loading spinner. The new component checks the isLoading prop and displays either the spinner or the wrapped component based on its value.

To use this HOC, you simply wrap your component with it:


const UserComponentWithSpinner = withLoadingSpinner(UserComponent);

Now, UserComponentWithSpinner will automatically show a loading spinner when the isLoading prop is true, without requiring any changes to UserComponent itself.

HOCs are particularly useful for cross-cutting concerns like logging, error handling, and authorization. They allow you to encapsulate these concerns in a single place and apply them to any component that needs them. For example, you could create an HOC that logs every time a component mounts or updates:


function withLogging(WrappedComponent) {
  return class extends React.Component {
    componentDidMount() {
      console.log(`Component ${WrappedComponent.name} mounted`);
    }

    componentDidUpdate() {
      console.log(`Component ${WrappedComponent.name} updated`);
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
}

By using withLogging, you can easily add logging to any component without altering its code. This makes it easy to track component lifecycle events across your application.

Another common use case for HOCs is enhancing components with additional data. For instance, you might want to fetch data from an API and pass it as props to a component. Instead of handling the data fetching logic in each component, you can create an HOC that takes care of it:


function withDataFetching(url) {
  return function(WrappedComponent) {
    return class extends React.Component {
      state = {
        data: null,
        loading: true,
        error: null
      };

      componentDidMount() {
        fetch(url)
          .then(response => response.json())
          .then(data => this.setState({ data, loading: false }))
          .catch(error => this.setState({ error, loading: false }));
      }

      render() {
        const { data, loading, error } = this.state;
        return (
          <WrappedComponent
            data={data}
            loading={loading}
            error={error}
            {...this.props}
          />
        );
      }
    };
  };
}

With withDataFetching, you can wrap any component to provide it with data from a specified URL. The wrapped component will receive data, loading, and error as props, allowing it to focus on rendering the UI based on these values.

While HOCs are incredibly powerful, they come with their own set of challenges. One potential issue is the "wrapper hell," where multiple HOCs are composed, leading to deeply nested component trees. This can make debugging more difficult, as it becomes harder to trace where certain props or state changes are coming from. To mitigate this, it's important to use HOCs judiciously and consider alternative patterns like React Hooks, which offer a more modern approach to sharing logic.

Another consideration is the naming of the wrapped component. By default, the name of the new component returned by an HOC is not very descriptive, which can make debugging more challenging. To address this, you can set a display name for the wrapped component:


function withLogging(WrappedComponent) {
  class WithLogging extends React.Component {
    componentDidMount() {
      console.log(`Component ${WrappedComponent.name} mounted`);
    }

    componentDidUpdate() {
      console.log(`Component ${WrappedComponent.name} updated`);
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  }

  WithLogging.displayName = `WithLogging(${getDisplayName(WrappedComponent)})`;
  return WithLogging;
}

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}

By setting a display name, you make it easier to identify the wrapped component in React DevTools and during debugging.

In conclusion, Higher Order Components are a powerful pattern for sharing logic and enhancing components in React. They enable developers to abstract common functionality, promote code reuse, and maintain a clean separation of concerns. While they come with some challenges, careful use of HOCs can greatly enhance the maintainability and scalability of your React applications. As you continue to build more complex applications, understanding and effectively utilizing HOCs will become an invaluable skill in your React toolkit.

Now answer the exercise about the content:

What is a Higher Order Component (HOC) in React?

You are right! Congratulations, now go to the next page

You missed! Try again.

Article image Code Splitting and Lazy Loading

Next page of the Free Ebook:

69Code Splitting and Lazy Loading

6 minutes

Earn your Certificate for this Course for Free! by downloading the Cursa app and reading the ebook there. Available on Google Play or App Store!

Get it on Google Play Get it on App Store

+ 6.5 million
students

Free and Valid
Certificate with QR Code

48 thousand free
exercises

4.8/5 rating in
app stores

Free courses in
video, audio and text