Skip to main content

Command Palette

Search for a command to run...

React Higher-Order Component Using Refs

Published
4 min read
React Higher-Order Component Using Refs

A higher-order component (HOC) is an advanced technique in React for reusing component logic. Basically, a higher-order component is a function that takes a component and returns a new component.

I like to consider myself pretty comfortable with HOCs. I've written quite a few of them in the years I've been using React. However, I came across an interesting error recently.

The Issue

Consider a HOC that, among other things, accesses the wrapped component via a ref. The HOC works without issue... until it's used to wrap a stateless functional component. Then, the following error is logged in the console:

Stateless function components cannot be given refs. Attempts to access this ref will fail.

So, a component might need most of the functionality of a HOC, just not the ref related code, and it can't use it!

You might be thinking, "Simple fix. I'd just make the component I'm wrapping a class-based component." Yes, that's one way. But how about a way where you can maintain your functional component? There had to be another solution.

The Solution

The solution lies in having the HOC be able to recognize a difference between a class-based component and a stateless functional component. In this case, it's the fact that class-based components extend React's Component class. Class-based components need to have a render method, so the existence of that method can determine whether or not a component is stateful. Once that is known, the wrapped component can be conditionally given a ref. See the code below for a stripped-down example of a HOC like this:

import React, { Component } from 'react';

export default EnhancedComponent => {
  return class SomeHOC extends Component {
    constructor() {
      super();

      // check the prototype of the wrapped component's prototype for render
      // if render is not undefined, then the component is stateful, and can have a ref
      this._isStateful = (EnhancedComponent.prototype.render !== undefined);
    }

    /* Other HOC code would go here */

    render() {
      const allProps = {
        // spread all passed in props
        // you'd also add any other props to pass to wrapped components here
        ...this.props
      };

      // if the component is stateful, give it a ref.  Yay!
      if (this._isStateful) {
        allProps.ref = node => { this._wrappedInstance = node; };
      }

      return (
        <EnhancedComponent
          {...allProps}
        />
      );
    }
  };
};

And there you have it! I know this might seem like a pretty specialized case, and it is. However, if you have a HOC that sometimes needs access to a ref while also being able to wrap stateless functional components, then this should work for you.