Structuring React Applications: Higher-Order Components

Do you ever find yourself frustrated with all the boilerplate and repetitive code in React components? Frequent use of patterns make components long-winded, hard to reason about, and difficult to maintain. And with mixins no longer supported in ES6 components, there is no longer any obvious solution!

This became a problem while writing Memamug. Without mixins, the code gradually evolved a bunch of annoying patterns, including:

  • passing unknown props through to child nodes
  • writing out long CSS class names
  • merging internal callbacks with those specified on props

But there is a solution: Higher-Order Components. And seeing how helpful I’ve found them, it is only natural that I want to spread the word!

What are Higher-Order components?

Higher-Order Components (HOCs) are JavaScript functions which add functionality to existing component classes. Just as React components let you add functionality to an application, Higher-Order Components let you add functionality to components. You could say they’re components for components.

Another way of thinking about HOCs is that they let you generate code automatically. You might be familiar with this concept from other languages. For example, Ruby programmers often call it metaprogramming.

Of course, the best way to understand HOCs is to see one in action!

A simple example

Let’s have a look at our first tiny HOC:

// See https://www.npmjs.com/package/except
import except from 'except';

function passthrough(component) {
    const passthroughProps = Object.keys(component.propTypes);

    return class extends component {
        render() {
            super.render(except(this.props, passthroughProps))
        }
    }
}

As with every HOC, passthrough is just a function; it takes a component as its argument, and returns a new component. To use it, just pass your component in:

class MyComponent extends React.component {
    render(passthrough) {
        //...
    }
}

MyComponent = passthrough(MyComponent);

You can also use HOCs as ES7 decorators:

@passthrough
class MyComponent extends React.component {
    render(passthrough) {
        //...
    }
}

Do you understand how MyComponent will differ after applying the passthrough HOC? Think about it for a bit, then touch or hover your mouse over the box below for an answer.


passthrough returns a new component which is an extension of your existing one. The new component defines a new render method – which calls the existing render method with an object called passthrough. This passthrough object is mostly identical to this.props, except that it doesn’t include any properties specified in the class’s propTypes object.

This HOC gives you a shortcut for passing props which your component doesn’t expect through to the underlying DOM components, without passing through props which you actually use.

In short, HOCs let you perform repetitive tasks programatically instead of by hand.

Great, but how do Higher-Order Components help me Structure My Application?

Where patterns involve writing out code with a single purpose multiple times across many components, HOCs allow you to confine code with a single aim to a single place. Or in fancy talk, HOCs can help your application achieve separation of concerns.

The flip side is that not all separation is good separation. It is also possible to use HOCs to spread related code over multiple arbitrary modules, setting up dependencies which make code harder to reason about and maintain.

Which begs the question:

How do I design useful Higher-Order Components?

The core rule is that quality HOCs do one thing, and do it well. A HOC should perform a task which is clearly defined, and unrelated to other tasks.

While designing HOCs, it may help to ask yourself the following questions:

  • Will component code be clearer when using the HOC? Well-designed HOCs indicate what they actually do; patterns spend a long time explaining how they do it.
  • Will the application be easier to maintain with the HOC? Confining code with a single purpose to a single location means updates to the code only need to happen in one place – not across the entire codebase.
  • Does the HOC have dependencies? Dependencies are not the end of the world, but consider whether they indicate that the HOC is doing too little or too much.
  • Can the HOC be re-used in other applications? Reusable components indicate good separation from the application’s internals.

You’ll also likely find that good HOCs reduce the amount of code in your application, but this is not always the case. In fact, factoring out tiny but common patterns may increase line count while still improving readability and maintainability.

The converse is also true; abstracting uncommon patterns may just be moving the mess somewhere else. The thing to remember here is that good design and less keystrokes are not the same thing.

But all the theory in the world isn’t going to help you design great HOCs. So let’s look at:

A real-world example

Memamug uses the Pacomo system for styling. Pacomo makes stylesheets much easier to reason about, but involves adding an application-wide prefix and the component’s JavaScript class name to every single CSS class. Given this pattern can be consistently used across multiple applications and involves a lot of repetition, it is a great candidate for hocification.

To start, let’s have a look at the render function in Memamug’s Paper class, without using any HOCs:

function render() {
    const className = `app-Paper app-Paper-${this.props.shape} ${this.props.className || ''}`;

    return (
        <div className={className}>
          <div className="app-Paper-inner">
            ...
          </div>
        </div>
    );
}

How would a HOC help us here? Well for a start, the app in each CSS class shouldn’t need to be repeated across the entire application. And manually writing out Paper for each class makes copying/pasting between components seriously error-prone. A HOC should also leave us with something a little more readable, showing us only the non-standard classes we’re adding instead of the ones which must be repeated over every component.

In short, it should allow us to write something like this:

@c('app')
function render() {
    return (
        <div className={this.cRoot(this.props.shape)}>
            <div className={this.cPrefix('inner')}>
                ...
            </div>
        </div>
    );
}

Exercise

Let’s test what you’ve learned by building the c higher order component which is used above. The following hints may help:

  • You can find the name of a component’s JavaScript class using it’s name property. For example, component.name.
  • You can add new methods to a component by adding them to it’s prototype object. For example, component.propotype.cPrefix = function() {};.

Have a go at implementing c, then touch or hover your mouse over the box below to see my answer.

function c(appPrefix) {
    return function(component) {
        const prefix = `${appPrefix}-${component.name}`;

        component.prototype.cPrefix = function(name) {
            return `${prefix}-${name}`;
        };

        component.prototype.cRoot = function(name) {
            return `${prefix} ${name ? prefix+'-'+name : ''} ${this.props.className || ''}` ;
        };
    }
}

Congratulations! You’ve built your first HOC.

Learn more by reading (and using) existing code. Lots of existing code.

In the process of building Memamug and Numbat UI, I’ve found a number of patterns which work well as HOCs. And lucky for you, they’re now on GitHub! Read and use them to get an intuitive grasp of HOCs, then make your own HOCs and send me links at @james_k_nelson!

  • react-c – A tool to help implement Pacomo, a system to add structure to your React stylesheets.
  • react-passthrough – Helps pass through unknown props to a child component
  • react-callback-register – Helps merge callbacks from props, your component, and decorators
  • react-base – Include react-c, react-passthrough and react-callback-register with a single decorator
  • react-base-control – Manages control-related events and state

And there will be more where these come from.

Find out about new Higher-Order Components so you don’t have to write them yourself.

I’m constantly looking for ways to improve my own projects, and when I find them, I like to share them as articles and code. So if you want to make top-quality apps without doing all the hard work yourself, make sure to sign up for my newsletter! And in return for your e-mail, you’ll also immediately receive 3 bonus print-optimised PDF cheatsheets – on React, ES6 and JavaScript promises.

I will send you useful articles, cheatsheets and code.

I won't send you useless inbox filler. No spam, ever.
Unsubscribe at any time.

Get in touch

Do you have something else you’d like to read more about, or other questions/comments? Send me an e-mail, or send @james_k_nelson a tweet. I seriously love hearing from readers!

Related reading

7 Comments Structuring React Applications: Higher-Order Components

  1. Egor

    Hey James,

    It’s a great article. Thank you!

    And did you think about component inheritance?

    For example, we have class Ticket extends React.Component … with basic markup.

    And, what if I want Ticket that could be edited but I do not want to create bunch of if/else in one component. I want to inherit from it.

    I could create class TicketEditable extends Ticket … And invoke super.render() inside render method.

    But! How could I modify this react element tree that was rendered after super.render had invoked?

    Reply
    1. Egor

      It seems that I found the answer.

      We could define ticketReactElement = super.render() and after that we could modify all his children just as:

      questionBodyDivElement.props.children.push(Bla-bla-bla)
      ;

      It’s awesome because we could inherit one from another and modify.

      Reply
      1. Egor

        sorry, ticketReactElement instead of questionBodyDivElement, of course

        render() {
        ticketReactElement.props.children.push(Bla-bla-bla);
        return ticketReactElement;
        }

        Reply
        1. Ivan

          Hey, that’s cool. I’m looking for a method to implement this, and your answer gives me the solution.

          Thanks a lot.

          Reply
  2. Marian

    This is soo much different from any other other examples of using HO func. All examples I’ve seen rendered decorated component in render method and used React.Component as base class. Your decorator extends directly decorating class and calling super.render instead. I like it.

    Reply

Leave a Reply

Your email address will not be published.