In a previous post I covered an example of a React mixin which I called ClickAway that detected clicks anywhere outside of a component. I also mentioned mixins are not React’s preferred method of reuse. Composition with components is the way to go, and mixins are on the way out.
With this in mind, I decided to convert the ClickAway mixin into a component. I was skeptical about how clean the new implementation would be, but it turned out well. As you’ll see, the equivalent component version better enables composition and is more explicit.
ClickAway as a Mixin
First, here’s the mixin code from my previous post. It exports an object with two React lifecycle methods: componentDidMount and componentWillUnmount. These methods set up and tear down the click detection code.
And here’s how you would use it. Notice the onClickAway
method in the popup component is required by the mixin, but there’s nothing explicitly defining this requirement. This is one of the weaknesses of a mixin. There is no explicit contract between it and the target component.
ClickAway as a Component
Migrating ClickAway to a component solves two issues. One, it enables composition, and two, it uses props and PropTypes to define an explicit contract.
Most of the code doesn’t change much. The helper functions hasId
and elementWithIdExists
port over as is. I used React.createClass
with the same implementations for the componentDidMount
and componentWillUnmount
lifecycle methods.
There are two differences. First, as a component, it needs to have a render
method. In this case it’s simple. ClickAway doesn’t alter the look of the UI so it simply returns its child elements.
render: function () {
return this.props.children;
}
Second, the onClickAway
callback is passed in as a prop value. This is more explicit than the mixin implementation where it is a method of the component. PropTypes further define it as a required function.
propTypes: {
onClickAway: React.PropTypes.func.isRequired
}
self.clickAway = function (event) {
if (!elementWithIdExists(event.target, elementId) &&
self.props.onClickAway) {
self.props.onClickAway(event);
}
};
Here’s the full implementation.
Using the Component
Now that it’s a component, ClickAway needs to be composed with other components. In the case of the popup, it wraps the return value of render()
with a <ClickAway>
JSX element. It also provides the onClickAway
callback as a prop value.
<ClickAway onClickAway={this.props.onClose}>
<div {...elementProps}>
{this.props.children}
</div>
</ClickAway>
Below is the new implementation of the popup component.
Components > Mixins
This exercise convinced me that components are indeed better than mixins. They enable composition and have an explicit contract. My next step is to convert the mixins in my current projects.