Higher-order Components: A React Application Design Pattern

Share this article

Higher-order Components: A React Application Design Pattern

In this article, we’ll discuss how to use higher-order components to keep your React applications tidy, well-structured and easy to maintain. We’ll discuss how pure functions keep code clean and how these same principles can be applied to React components.

Pure Functions

A function is considered pure if it adheres to the following properties:

  • all the data it deals with is declared as arguments
  • it doesn’t mutate data it was given or any other data (these are often referred to as side effects)
  • given the same input, it will always return the same output.

For example, the add function below is pure:

function add(x, y) {
  return x + y;
}

However, the function badAdd below is impure:

let y = 2;
function badAdd(x) {
  return x + y;
}

This function is not pure because it references data that it hasn’t directly been given. As a result, it’s possible to call this function with the same input and get different output:

let y = 2;
badAdd(3) // 5
y = 3;
badAdd(3) // 6

To read more about pure functions you can read “An introduction to reasonably pure programming” by Mark Brown.

Whilst pure functions are very useful, and make debugging and testing an application much easier, occasionally you’ll need to create impure functions that have side effects, or modify the behavior of an existing function that you’re unable to access directly (a function from a library, for example). To enable this, we need to look at higher-order functions.

Higher-order Functions

A higher-order function is a function that returns another function when it’s called. Often they also take a function as an argument, but this isn’t required for a function to be considered higher-order.

Let’s say we have our add function from above, and we want to write some code so that when we call it, we log the result to the console before returning the result. We’re unable to edit the add function, so instead we can create a new function:

function addAndLog(x, y) {
  const result = add(x, y);
  console.log(`Result: ${result}`);
  return result;
}

We decide that logging results of functions is useful, and now we want to do the same with a subtract function. Rather than duplicate the above, we could write a higher-order function that can take a function and return a new function that calls the given function and logs the result before then returning it:

function logAndReturn(func) {
  return function(...args) {
    const result = func(...args)
    console.log('Result', result);
    return result;
  }
}

Now we can take this function and use it to add logging to add and subtract:

const addAndLog = logAndReturn(add);
addAndLog(4, 4) // 8 is returned, ‘Result 8’ is logged

const subtractAndLog = logAndReturn(subtract);
subtractAndLog(4, 3) // 1 is returned, ‘Result 1’ is logged;

logAndReturn is a higher-order function because it takes a function as its argument and returns a new function that we can call. These are really useful for wrapping existing functions that you can’t change in behavior. For more information on this, check M. David Green’s article “Higher-Order Functions in JavaScript”, which goes into much more detail on the subject.

See the Pen
Higher Order Functions
by SitePoint (@SitePoint)
on CodePen.

Higher-order Components

Moving into React land, we can use the same logic as above to take existing React components and give them some extra behaviors.

Note: with the introduction of React Hooks, released in React 16.8, higher-order functions became slightly less useful because hooks enabled behavior sharing without the need for extra components. That said, they are still a useful tool to have in your belt.

In this section, we’re going to use React Router, the de facto routing solution for React. If you’d like to get started with the library, I highly recommend the React Router documentation as the best place to get started.

React Router’s Link component

React Router provides a <NavLink> component that’s used to link between pages in a React application. One of the properties that this <NavLink> component takes is activeClassName. When a <NavLink> has this property and it’s currently active (the user is on a URL that the link points to), the component will be given this class, enabling the developer to style it.

This is a really useful feature, and in our hypothetical application we decide that we always want to use this property. However, after doing so we quickly discover that this is making all our <NavLink> components very verbose:

<NavLink to="/" activeClassName="active-link">Home</NavLink>
<NavLink to="/about" activeClassName="active-link">About</NavLink>
<NavLink to="/contact" activeClassName="active-link">Contact</NavLink>

Notice that we’re having to repeat the class name property every time. Not only does this make our components verbose, it also means that if we decide to change the class name we’ve got to do it in a lot of places.

Instead, we can write a component that wraps the <NavLink> component:

const AppLink = (props) => {
  return (
    <NavLink to={props.to} activeClassName="active-link">
      {props.children}
    </NavLink>
  );
};

And now we can use this component, which tidies up our links:

<AppLink to="/home" exact>Home</AppLink>
<AppLink to="/about">About</AppLink>
<AppLink to="/contact">Contact</AppLink>

In the React ecosystem, these components are known as higher-order components, because they take an existing component and manipulate it slightly without changing the existing component. You can also think of these as wrapper components, but you’ll find them commonly referred to as higher-order components in React-based content.

Better Higher-order Components

The above component works, but we can do much better. The <AppLink> component that we created isn’t quite fit for purpose.

Accepting multiple properties

The <AppLink> component expects two properties:

  • this.props.to, which is the URL the link should take the user to
  • this.props.children, which is the text shown to the user

However, the <NavLink> component accepts many more properties, and there might be a time when you want to pass extra properties along with the two above, which we nearly always want to pass. We haven’t made <AppLink> very extensible by hard coding the exact properties we need.

The JSX spread

JSX, the HTML-like syntax we use to define React elements, supports the spread operator for passing an object to a component as properties. For example, the code samples below achieve the same thing:

const props = { a: 1, b: 2};
<Foo a={props.a} b={props.b} />

<Foo {...props} />

Using {...props} spreads each key in the object and passes it to Foo as an individual property.

We can make use of this trick with <AppLink> so we support any arbitrary property that <NavLink> supports. By doing this we also future proof ourselves; if <NavLink> adds any new properties in the future our wrapper component will already support them.

const AppLink = (props) => {
  return <NavLink {...props} activeClassName="active-link" />;
}

Now <AppLink> will accept any properties and pass them through. Note that we also can use the self-closing form instead of explicitly referencing {props.children} in between the <NavLink> tags. React allows children to be passed as a regular prop or as child elements of a component between the opening and closing tag.

Property ordering in React

Imagine that for one specific link on your page, you have to use a different activeClassName. You try passing it into <AppLink>, since we pass all properties through:

<AppLink to="/special-link" activeClassName="special-active">Special Secret Link</AppLink>

However, this doesn’t work. The reason is because of the ordering of properties when we render the <NavLink> component:

return <Link {...props} activeClassName="active-link" />;

When you have the same property multiple times in a React component, the last declaration wins. This means that our last activeClassName="active-link" declaration will always win, since it’s placed after {...this.props}. To fix this, we can reorder the properties so we spread this.props last. This means that we set sensible defaults that we’d like to use, but the user can override them if they really need to:

return <Link activeClassName="active-link" {...props}  />;

By creating higher-order components that wrap existing ones but with additional behavior, we keep our code base clean and defend against future changes by not repeating properties and keeping their values in just one place.

Higher-order Component Creators

Often you’ll have a number of components that you’ll need to wrap in the same behavior. This is very similar to earlier in this article when we wrapped add and subtract to add logging to them.

Let’s imagine in your application you have an object that contains information on the current user who is authenticated on the system. You need some of your React components to be able to access this information, but rather than blindly making it accessible for every component you want to be more strict about which components receive the information.

The way to solve this is to create a function that we can call with a React component. The function will then return a new React component that will render the given component but with an extra property which will give it access to the user information.

That sounds pretty complicated, but it’s made more straightforward with some code:

function wrapWithUser(Component) {
  // information that we don’t want everything to access
  const secretUserInfo = {
    name: 'Jack Franklin',
    favouriteColour: 'blue'
  };

  // return a newly generated React component
  // using a functional, stateless component
  return function(props) {
    // pass in the user variable as a property, along with
    // all the other props that we might be given
    return <Component user={secretUserInfo} {...props} />
  }
}

The function takes a React component (which is easy to spot given React components commonly have capital letters at the beginning) and returns a new function that will render the component it was given with an extra property of user, which is set to the secretUserInfo.

Now let’s take a component, <AppHeader>, which wants access to this information so it can display the logged-in user:

const AppHeader = function(props) {
  if (props.user) {
    return <p>Logged in as {props.user.name}</p>;
  } else {
    return <p>You need to login</p>;
  }
}

The final step is to connect this component up so it’s given this.props.user. We can create a new component by passing this one into our wrapWithUser function:

const ConnectedAppHeader = wrapWithUser(AppHeader);

We now have a <ConnectedAppHeader> component that can be rendered, and will have access to the user object.

I chose to call the component ConnectedAppHeader because I think of it as being connected with some extra piece of data that not every component is given access to.

This pattern is very common in React libraries, particularly in Redux, so being aware of how it works and the reasons it’s being used will help you as your application grows and you rely on other third-party libraries that use this approach.

Conclusion

This article has shown how, by applying principles of functional programming such as pure functions and higher-order components to React, you can create a codebase that’s easier to maintain and work with on a daily basis.

By creating higher-order components, you’re able to keep data defined in only one place, making refactoring easier. Higher-order function creators enable you to keep most data private and only expose pieces of data to the components that really need it. By doing this you make it obvious which components are using which bits of data, and as your application grows you’ll find this beneficial.

If you have any questions, I’d love to hear them. Feel free to ping me @Jack_Franklin on Twitter.

FAQs About Higher Order Components (HOC) in React

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

A Higher Order Component (HOC) in React is a pattern where a component is wrapped with a function to enhance its functionality or behavior. This function takes a component as an argument and returns a new component with additional props, state, or other features.

How does a Higher Order Component enhance component functionality?

A Higher Order Component can add new props, modify existing props, manage state, or encapsulate certain behaviors. It allows for the reusability of component logic and promotes a modular and scalable architecture in React applications.

What are some common use cases for Higher Order Components?

Common use cases for HOCs include authentication, data fetching, logging, and code reuse. For example, an authentication HOC could protect a component and ensure that only authenticated users can access it.

How do you create a Higher Order Component in React?

To create an HOC, you define a function that takes a component as an argument and returns a new component with enhanced functionality. This can involve manipulating props, managing state, or wrapping the original component in additional logic.

Can a component be wrapped by multiple Higher Order Components?

Yes, it is possible to wrap a component with multiple HOCs. This is known as composing HOCs, where each HOC contributes a specific set of behaviors or features to the wrapped component.

Are Higher Order Components a replacement for Render Props in React?

Both HOCs and Render Props are patterns for code reuse in React, but they have different implementations. HOCs involve wrapping a component, while Render Props involve passing a function as a prop to share code. The choice between them often depends on the specific use case and developer preference.

Are there any potential drawbacks to using Higher Order Components?

While HOCs offer a powerful way to reuse component logic, they can lead to prop conflicts and make the component hierarchy less transparent. Additionally, using too many HOCs can make the code harder to understand. Careful consideration and testing are essential when implementing HOCs.

Jack FranklinJack Franklin
View Author

I'm a JavaScript and Ruby Developer working in London, focusing on tooling, ES2015 and ReactJS.

compositionnilsonjReactreact-hubReact-Learnreactjs
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week