Higher-Order Components

Learn how higher-order components work and how to make them.

Refactoring with higher-order components

The power of React is that it allows you to express web apps in individual units called components. But the rules for assigning different bits of functionality to different components aren’t always clear. In principle, any React app could be expressed as a single monolithic component. Or, at the opposite extreme, every DOM element in the page could be managed by its own micro-component.

A good rule of thumb is that components should be built so that each component has only one job. Components with multiple responsibilities are a good candidate for being split up. These distinctions are intuitive, not technical. No automated process is going to tell you whether a component has multiple responsibilities. Still, thinking in these terms will help you as you work to keep code manageable. The more complex an individual component is, the harder it’ll be to make changes to it.

This chapter is about splitting up complex components into simpler pieces using a pattern called higher-order components (HOCs). You’ll add new functionality to the carousel component from the previous chapter while actually simplifying the core component by extracting some of its logic to an HOC. And you’ll learn how to use the React Devtools to see how different components are interacting in your browser.

Making higher-order components

In the abstract, a higher-order component is defined as any function that takes a component and returns another component. Typically, the component returned by the HOC function is a wrapper around the original component that adds functionality. Here’s a simple example:

const BindProps = (Component, boundProps) => { // 1
  const ComponentWithBoundProps = props => (
    <Component {...props} {...boundProps} /> //2 
  );
  ComponentWithBoundProps.displayName =
    `BindProps(${Component.displayName || Component.name})`; // 3
  return ComponentWithBoundProps;
};

const CarouselWithTestAttr = BindProps(Carousel, { // 4
  'data-test-id': 'carousel',
});
  1. The BindProps HOC takes two arguments, a component and a props object, to “bind” to that component.
  2. Since the boundProps passed to the HOC are spread into the component after the props is passed directly, the boundProps takes precedence.
  3. displayName is a special property that gives React components a name for debugging purposes. We haven’t had to deal with it so far because JavaScript functions and classes have a name property that usually works as a fallback. But for a dynamically generated component like this one, you’ll need to set the name manually. Using a name with the form "HOC(Component)" is a common convention.
  4. Here, BindProps is used to generate a component that behaves exactly like Carousel, except that it will always receive data-test-id="carousel".

Get hands-on with 1200+ tech skills courses.