React Higher Order Component Patterns

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

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

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

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

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

  • 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.

Web Developer, Oregonian, husband