All Articles

Getting rid of HOC's

The eTender widget uses React Higher Order Components in various places to facilitate code reuse. HOC’s are the first pattern you find if you look for ways to reuse code in React.js. But after reading ‘Use a Render Prop!’, by Michael Jackson, I got convinced that they are not always the best choice so I decided to change the HOC’s to regular components with a ‘render prop’.

In this blog post I’ll outline the changes that have to be made to get rid of HOC’s.

The hoverable HOC

In the eTender widget there are multiple components (party size, time, date) that are hoverable and change their style when hovered. At first I used this simple hoverable HOC, inspired by this github gist.

// HOC:
import React, { PureComponent } from 'react'

function hoverable(WrappedComponent, propName = 'hover') {
  return class HoverableComponent extends PureComponent {
    state = { hovered: false }

    toggleHovered(toggle) {
      this.setState({ hovered: toggle })
    }
    render() {
      const props = { [propName]: this.state.hovered, ...this.props }
      return (
        <div
          onMouseEnter={() => this.toggleHovered(true)}
          onMouseLeave={() => this.toggleHovered(false)}
        >
          <WrappedComponent {...props} />
        </div>
      )
    }
  }
}

export default hoverable

This HOC was then used like this:

import React from 'react'
import hoverable from '../common/hoverable'

const PartySizeCell = props => (
  <div
    className={`${props.hover ? 'etender-calendar-widget-theme' : ''}`}
    onClick={() => props.onPartySizeClick(props.index)}
  >
    {props.index}
  </div>
)

const HoverablePartySizeCell = hoverable(PartySizeCell)

export default HoverablePartySizeCell

Convert to ‘Render props’

It is very simple to get rid of the HOC. You just change the HOC to a regular component with a ‘render prop’.

import React from "react";
import PropTypes from "prop-types";

class HoverableComponent extends React.Component {
    static propTypes = {
        render: PropTypes.func.isRequired
    };

    state = { hovered: false };

    toggleHovered(toggle) {
        this.setState({ hovered: toggle });
    }

    render() {
        return (
            <div
                onMouseEnter={() => this.toggleHovered(true)}
                onMouseLeave={() => this.toggleHovered(false)}>
                {this.props.render(this.state)}
            </div>
        );
    }
}

export default HoverableComponent;

This normal component is then used like this:

import React from 'react'
import HoverableComponent from '../HoverableComponent'

const PartySizeCell = props => (
  <HoverableComponent
    render={({ hovered }) => (
      <div
        className={`${hovered ? 'etender-calendar-widget-theme' : ''}`}
        onClick={() => props.onPartySizeClick(props.index)}
      >
        {props.index}
      </div>
    )}
  />
)

export default PartySizeCell

Conclusion

As you can see it is very easy to transform HOC’s to normal React Components. By doing this you get a lot of benefits, which are described in detail in ‘Use a Render Prop!’, by Michael Jackson.

Published 31 Jul 2018

A blog about .net core and full stack web development.
Peter Dolon GitHub