React Higher Order Component Patterns

Nick Gard
4 min readJan 21, 2018

If you spend much time writing React components, you’ll run across Higher Order Components (HOCs) and, eventually, you’ll want to write your own. Why will you write one? Partly because they’re cool but mostly because they’re a useful abstraction. Here’s when you know that you want to use or write an HOC: when you want to abstract away behavior. Much like how a regular component abstracts away markup and styling, an HOC allows you to share behavior between components. What are some of the behaviors that you’ll want to share? Injected dependencies (props), modified or alternative markup, and common lifecycle functions are the types of things I have used and written HOCs to share. There are three patterns that I use to do these and each one has its own prerequisites and use-cases.

First things first

But first, a rule that all HOCs should honor: an HOC should consume one component and return one component. By this I mean that using a HOC should always look like this:

const EnhancedComponent = myHOC(Component);
// ...later used as <EnhancedComponent />

If your HOC needs configuration, such as does the venerable connect from Redux, then your HOC should consume the configuration(s) and return a function that consumes one component and returns one component. (Maybe we should call this a Higher HOC, an HHOC.) Let me be clear: if your function consumes multiple components, or a component and other values at the same time then your function is not an HOC.

The reason for this rule is simple: HOCs should be composable. Using something like Recompose’s compose function (an HHOC), you should be able to write:

const SuperEnhancedComponent = compose(
myHOC,
myHHOC(configObj),
)(BoringComponent);

Functional HOCs

Just like regular React components, if all you want to do is inject some props or provide an alternative rendering without relying on lifecycle functions, then this is the pattern for you. I would go as far as to say that this should be your base pattern, your default. Only move on to the others if this one doesn’t work for your use-case. Here is an example of one I wrote that only renders the passed component if the DOM exists (in other words, this prevents the component from rendering server-side where DOM APIs that the component may rely on don’t exist.)

function onClientOnly(Component) {
if (/* check for DOM */) {
return Component;
}
return null;
}

Here’s a slightly more complicated one that injects a specific style into the passed component:

function withDarkTheme(Component) {
return ({ style, ...rest }) => (
<Component style={{ ...darkThemeObj, ...style }} {...rest} />
);
}

The pattern is pretty simple, but powerful. Again, this should be your go-to way of writing HOCs.

Class HOCs

What if you need to manage the lifecycle of a prop that you need to inject? Just like with regular React components, you’ll need to reach for a Class. Similar to the Functional HOCs, you’ll begin with a function that takes a component, but this time you’ll return a new class-based component that renders the passed component in the render() method. Here is one that adds a listener for an ESC key click and passes an onEsc prop:

function withEscapeHandler(Component) {
class componentWithEscapeHandler extends React.Component {
constructor(props) {
super(props);
this.handleKeypress = this.handleKeypress.bind(this);
}
handleKeypress(event) {
if (event.keyCode === 27) {
this.props.onEsc(event);
}
}
componentDidMount() {
window.addEventListener('keypress', this.handleKeypress);
}
componentWillUnmount() {
window.removeEventListener('keypress', this.handleKeypress);
}
render() {
return (
<Component onEsc={this.handleKeypress} {...this.props} />
);
}
}
return componentWithEscapeHandler;
}

Inheritance Inversion HOCs

This is the last, and least-used, pattern. It should only be used when you need access to the passed component’s internals, like its state and methods. That should sound like a red flag to you because it is. Components should be black boxes for the most part, adhering to the maxim “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. Using Inheritance Inversion opens up and directly modifies the component.

To do this, write a Class HOC whose returned component extends the passed component instead of React.Component. This is what gives the HOC access to the component’s internals. A key thing to remember when writing an Inheritance Inversion HOC is that each method from the parent can be accessed from the super object. I have only found one use-case for this:

function withFunctionAsAChild(Component) {
class ComponentWithFACC extends Component {
render() {
const { children, ...props } = this.props;
if (typeof children === 'function') {
return children(props, this.state, this);
}
return super.render();
}
}
return ComponentWithFACC;
}

Final Touches

Much like how React Components exist to abstract common markup, Higher Order Components exist to abstract common behavior. When considering creating any abstraction, check that a pattern already exists before pulling out the smallest cohesive repetition into the abstraction. Use HOCs to inject props, render alternative markup, and/or manage lifecycle methods. I recommend also following these rules:

  • Name each function and class to make debugging easier. See the React docs on this.
  • Pass all unused props through to the underlying component.
  • Use hoistNonReactStatics in every HOC to preserve the underlying component’s static properties. See the React docs on that.

--

--