In this post I will discuss the why and how to use higher-order components (HOC) with React JS.

Why use HOC: Promote reuse of logic across React components.

Components are the typical element for reuse in React but sometimes features don’t fit into this standard. There might be similar methods used to fetch data but the display is different. An example of this is shown later.

Sandy beach path straw fence Photo by Erda Estremera on Unsplash

How to use HOC: The core structure of a HOC is a function that takes a component as the first parameter and returns a function wrapping the first parameter. HOC are pure functions with no side-effects because the component passed in, is wrapped in a new component. Typically data is injected as a prop and additional props are appended to the component.

Example of a basic HOC:

function withExample(Component) {
  return function(props) {
    return <Component />;
  };
}

This is a general compositional pattern and not part of React as such.

How do you decide to use HOCs?

I would recommend first building components in the normal React way. When the application is working as expected, review your components to identify shared behaviours. Build the behaviour in a generic enough fashion to work for all existing components. Following this will improve your experience in identifing HOCs even before completing components, but I would hold back and treat it as a refactoring step when deciding to build a new HOC.

Fetch data higher-order components

Firstly, here’s the working components which have similar data fetching behaviour:

class VideoBlog extends React.Component {
  constructor() {
    super();
    this.state = {
      videoBlog: null
    };
  }

  componentDidMount() {
    fetch("http://example.com/videos/124").then(data => {
      this.setState({
        videoBlog: data.videoBlog
      });
    });
  }

  render() {
    return <video src={this.state.videoBlog.src} />;
  }
}

class RelatedVideos extends React.Component {
  constructor() {
    super();
    this.state = {
      videoList: []
    };
  }

  componentDidMount() {
    fetch("http://example.com/videos/related/" + this.props.videoId).then(
      data => {
        this.setState({
          videoList: data.videoList
        });
      }
    );
  }

  render() {
    return <List data={this.state.videoList} />;
  }
}

VideoBlog and RelatedVideo are not the same output but the implementation is similar. They both fetch data in a componentDidMount life cycle.

What will the above components look like when wrapped in withFetch HOC?

const VideoBlog = withFetch(VideoBlogView, "http://example.com/videos/124");
const RelatedVideo = withFetch(
  RelatedVideoView,
  "http://example.com/videos/related/"
);

Breakdown of the above

First param is the component to be wrapped by the HOC. The old VideoBlog component changed from a class to a functional component called VideoBlogView because it no longer needs to manage state. The same applies to RelatedVideo.

Second param is the fetch url to get data from.

The withFetch HOC looks like this:

function withFetch(WrapComponent, request) {
  class WithFetch extends React.Component {
    constructor() {
      super();
      this.state = {
        data: null
      };
    }

    componentDidMount() {
      let url = request;
      if (this.props.videoId !== undefined) url = request + this.props.videoId;

      fetch(url).then(data => {
        this.setState({
          data
        });
      });
    }

    render() {
      return <WrapComponent response={this.state.data} {...this.props} />;
    }
  }

  WithFetch.displayName = `WithFetch(${getDisplayName(WrapComponent)})`;
  return WithFetch;
}

State now has a data property to inject data from the API fetch into the wrapped component via the response prop.

ComponentDidMount has a more generic fetch call to get data from any URL.

A good convention in HOCs is to set the displayName with the name of HOC function (withFetch) and component display name (VideoBlog), as this helps with debugging.

Libraries using HOCs

Below are some libraries you might have used which are using HOCs.

Relay.createContainer(component, graphqlQuery); RelayJS createContainer follows a similar function signature to what is shown in my example above. First param is the component to be wrapped and second is the query.

ReactRedux.connect(props, dispatch)(component); React Redux connect has a different function signature of a function returning a function which accepts one argument (ie, ‘component’) to create a HOC. This is more complex however you might see more of this style because this promotes HOC composition (also called enhancers).

A library that promotes HOC composition is Recompose.

A React utility belt for function components and higher-order components.

In the first part of Recompose documentation they provide a good explanation about enhancers

It’s worth pointing out that there is a minor performance loss when you start composing with many HOCs.

Conclusion

Once you gain experience in building higher-order components you will start to notice your components become smaller and more focused units of code. This in turn will help make it easier to reason about your application logic. Reusing HOCs with composition means it will be a matter of plugging in relevant components to build complex features with ease.

Try what you have learned

I’ve created a code challenge on Codewars to build your own React JS higher-order component kata. Please rate the kata when you complete it.

Next: Understand React JS Render Props