Higher-Order Components (HOC) in React: How to Use and Real-World Applications

VnnTools

In the world of React programming, code optimization and reusability are always top priorities. Among countless design patterns, Higher-Order Component (HOC) emerges as a powerful and classic technique, helping developers abstract and share logic between components efficiently.

Higher-Order Components (HOC) in React: How to Use and Real-World Applications

So what is HOC in React? How does it work and when should we use it? Let's explore this concept comprehensively through the article below!

What is HOC? An Intuitive Look

Imagine you have a t-shirt manufacturing factory. This factory takes a plain t-shirt (a component) and "decorates" it with new features like printing designs, adding pockets, or changing colors. The final product is still a t-shirt, but it has been upgraded with new characteristics.

HOC (Higher-Order Component) in React operates on a similar philosophy.

HOC is a function that takes a Component and returns a new Component that has been "enhanced."

More technically, HOC is an advanced design pattern in React that allows for component logic reuse. HOC is not part of the React API, but rather a pattern that emerged from React's functional programming nature.

The basic syntax of an HOC looks like this:

const enhancedComponent = higherOrderComponent(WrappedComponent)

Where:

  • higherOrderComponent: Is our HOC function.
  • WrappedComponent: Is the original component we want to "wrap" or "enhance."
  • enhancedComponent: Is the new component that contains the HOC logic and renders WrappedComponent inside.

How does the "inside" of an HOC work?

To understand better, let's build a classic example: creating an HOC called withLoading to display a "Loading..." message while the component is waiting for data.

Suppose we have a ProductList component responsible for fetching and displaying a list of products.

// ProductList.js
import React, { useState, useEffect } from 'react'

const ProductList = () => {
  const [products, setProducts] = useState([])
  const [isLoading, setIsLoading] = useState(true)

  useEffect(() => {
    // Simulate data fetching
    setTimeout(() => {
      setProducts(['Product 1', 'Product 2', 'Product 3'])
      setIsLoading(false)
    }, 2000)
  }, [])

  if (isLoading) {
    return <div>Loading...</div>
  }

  return (
    <ul>
      {products.map((product) => (
        <li key={product}>{product}</li>
      ))}
    </ul>
  )
}

export default ProductList

Now, if we have another component, such as UserList, that also needs similar "Loading..." logic, repeating the isLoading code is not optimal. This is exactly when HOC shines.

We'll create the withLoading HOC:

// withLoading.js
import React from 'react'

const withLoading = (WrappedComponent) => {
  // HOC returns a new class component or functional component
  return class WithLoading extends React.Component {
    render() {
      // Receive an `isLoading` prop and decide what to render
      const { isLoading, ...props } = this.props

      if (isLoading) {
        return <div>Loading data, please wait...</div>
      }

      // If not loading, render the original component with its props
      return <WrappedComponent {...props} />
    }
  }
}

export default withLoading

Now, we can apply this HOC to any component:

// ProductListWithLoading.js
import withLoading from './withLoading'
import ProductList from './ProductList' // Original component no longer needs loading logic

const ProductListWithLoading = withLoading(ProductList)

export default ProductListWithLoading

In the parent component, we just need to pass the isLoading prop:

// App.js
function App() {
  const [loading, setLoading] = useState(true);

  // ... logic for fetching data and setting loading...

  return <ProductListWithLoading isLoading={loading} products={...} />;
}

As you can see, the withLoading HOC has helped us completely separate the logic for displaying data loading states from the component, making the code clean, maintainable, and reusable.

Real-world applications of HOC

HOC is not just for displaying loading states. It's extremely flexible and can be used in many different scenarios:

  • Props manipulation and control: Adding or modifying props passed to the wrapped component.
  • State abstraction and management: HOC can manage state and pass it down to child components as props. For example: HOC managing the open/close state of a modal.
  • Authentication and authorization: Wrapping routes or components with a requireAuth HOC to check if the user is logged in. If not, redirect them to the login page.
  • Store connection (Redux): The connect function from react-redux is the most classic example of HOC. It takes a component and "injects" state from the Redux store and action creators into that component's props.
  • Logging and tracking: Creating an HOC to log lifecycle events or user interactions within a component.
  • Context provision: HOC can read values from React Context and pass them as props to components that don't need to directly use Context.Consumer.

HOC vs. Hooks: When to use which?

The emergence of React Hooks (especially useState, useEffect, useContext) has changed how we write components and reuse logic. Hooks provide a more direct and simpler approach than HOC in many cases.

So, have Hooks made HOC obsolete? The answer is no.

Higher-Order Components (HOC)

  • Nature: A function that wraps a component.
  • Usage: const EnhancedComponent = withHOC(MyComponent);
  • Advantages:
    • Works well with both class components and functional components.
    • Very powerful in abstracting complex logic and restructuring components.
  • Disadvantages:
    • Can create "wrapper hell."
    • Difficult to track the origin of props (which props come from which HOC?).
    • Syntax is somewhat more verbose than Hooks.

React Hooks

  • Nature: Special functions that allow "hooking into" React features.
  • Usage: const value = useCustomHook();
  • Advantages:
    • Concise syntax, easy to read and understand.
    • Avoids "wrapper hell" when multiple HOCs are nested.
    • Logic is placed directly inside the component, easier to track.
  • Disadvantages:
    • Only works in functional components.
    • Must follow Hooks rules (only call at top-level, don't call in loops/conditions).

When should you choose HOC?

  • When you need to reuse the same logic for many class components.
  • When you want to change how a component renders from the outside thoroughly (e.g., adding wrapper elements, completely changing DOM structure).
  • When working with older libraries or large codebases that still widely use HOC (like older versions of react-redux).

When should you choose Hooks?

  • In most cases of modern React application development with functional components.
  • When you want to share stateful logic in a simple and direct way.
  • To avoid the complexity of nesting multiple HOCs.

In reality, HOC and Hooks can coexist and complement each other perfectly in the same project.

Conclusion: HOC is not obsolete

Higher-Order Component (HOC) is a powerful design pattern and an important part of React's development history. Although the popularity of React Hooks has brought simpler alternatives for many problems, HOC still maintains a solid position in solving structured logic reuse issues, especially in complex scenarios or when working with class components.

Understanding HOC not only helps you master a powerful tool but also provides deeper insights into React's architecture and capabilities. Good luck applying this technique to your projects!

Related Posts

Props in React: Concepts, Usage and Real-world Examples

Decode props in ReactJS: from basic concepts to effectively passing data between components. Explore easy-to-understand examples that you can apply immediately.

Two-way Binding in React: How It Works & Easy-to-Understand Examples

Detailed guide on two-way binding in React. This article analyzes how it works, explains the differences compared to other frameworks, and provides practical examples.

React Lazy Loading: An Extremely Effective Technique for Accelerating Web Applications

Learn React Lazy Loading to improve application performance, reduce page load time, and optimize user experience. Detailed guide for ReactJS developers.

What is a React Component? A Detailed Guide for Beginners

Understanding React Components is the key to building modern web application interfaces. Read now to master common component types and how to create your first component.