How to Update Higher-Order Components in React

Higher-Order Components (HOCs) are an advanced pattern in React used for reusing component logic by wrapping an existing component and returning a new one. This article explains how to update HOCs when prop changes occur, manage internal state updates within the container component, and successfully migrate legacy HOCs to modern React patterns like Custom Hooks.

Updating HOCs When Props Change

An HOC acts as a container for the wrapped component. To ensure the wrapped component updates correctly when the parent component’s props change, the HOC must pass all incoming props through to the wrapped component.

If you filter out or modify props inside the HOC, you must ensure that any updates to the remaining props are still spread down.

import React from 'react';

// An HOC that adds a loading spinner capability
const withLoading = (WrappedComponent) => {
  return class WithLoading extends React.Component {
    render() {
      const { isLoading, ...passThroughProps } = this.props;

      // When isLoading updates, this render method triggers
      if (isLoading) {
        return <div>Loading...</div>;
      }

      // Pass all other updated props down to the wrapped component
      return <WrappedComponent {...passThroughProps} />;
    }
  };
};

export default withLoading;

By using the rest parameter syntax (...passThroughProps), React automatically forwards any updated props to the wrapped component, triggering a re-render with the new data.

Updating Internal State Inside an HOC

To update the state of an HOC dynamically, you treat it like any other React component. When the HOC’s internal state updates via setState (in class components) or useState (in functional components), it forces a re-render of both the HOC and the wrapped component with the updated values.

Here is an example of updating a functional HOC using React state:

import React, { useState, useEffect } from 'react';

const withSubscription = (WrappedComponent, selectData) => {
  return function WithSubscription(props) {
    const [data, setData] = useState(null);

    useEffect(() => {
      function handleChange(newData) {
        // Updating state triggers a re-render with the new data
        setData(newData);
      }

      DataSource.addChangeListener(handleChange);
      return () => {
        DataSource.removeChangeListener(handleChange);
      };
    }, []);

    return <WrappedComponent data={data} {...props} />;
  };
};

Whenever DataSource triggers an update, setData is called, updating the HOC’s state and passing the fresh data down to the wrapped component.

Modernizing HOCs: Migrating to React Hooks

In modern React, Higher-Order Components are often replaced by Custom Hooks. Hooks solve the same problem—reusing stateful logic—without nesting components in the React DOM tree. Updating your codebase from HOCs to Hooks makes your components cleaner and easier to maintain.

Step 1: Identify the HOC Logic

Take the logic inside your HOC (such as lifecycle methods, state, and event listeners) and isolate it.

Step 2: Create a Custom Hook

Extract that logic into a function starting with “use”.

// Custom Hook replacing the subscription HOC
import { useState, useEffect } from 'react';

function useSubscription() {
  const [data, setData] = useState(null);

  useEffect(() => {
    function handleChange(newData) {
      setData(newData);
    }
    DataSource.addChangeListener(handleChange);
    return () => DataSource.removeChangeListener(handleChange);
  }, []);

  return data;
}

Step 3: Consume the Hook in the Component

Instead of wrapping your component with an HOC, call the Custom Hook directly inside your functional component.

import React from 'react';
import useSubscription from './useSubscription';

function MyComponent(props) {
  const data = useSubscription();

  if (!data) {
    return <div>Loading...</div>;
  }

  return <div>Data: {data.value}</div>;
}

export default MyComponent;

Updating your React architecture from HOCs to Hooks eliminates “wrapper hell,” improves TypeScript type definitions, and aligns your application with the future of React development.