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