Sweet HOCs: withHoverState
A reusable way to write hover effects in React.
If you’re a UX nut, you’ve probably implemented some sort of “hover” effect. If you’re a React developer, you’ve probably implemented hover effects using the onMouseEnter
and onMouseLeave
events on a DOM element.
Unfortunately, managing this state over and over can be painful, and add bloat to your React application. What if there was a way to automagically know whether our component is being hovered?
Enter React Powerplug
React Powerplug is a utility library full of components that use render props to help you write fully controlled components without managing state yourself. This is immensely helpful when writing pure functional components and composing additional state, as opposed writing class-based components and managing this.state
yourself.
First, let’s look at how we normally have to manage hover state on a div
.
// BasicHoverExample.jsimport React, { Component } from 'react'class BasicHoverExample extends Component {
state = {
hovered: false
} this.updateHoverState = nextHoverState =>
this.setState({ hover: nextHoverState }) render() {
return (
<div
onMouseEnter={() => this.updateHoverState(true)}
onMouseLeave={() => this.updateHoverState(false)}
style={{
backgroundColor: this.state.hovered ? 'red' : 'blue'
}}
>
{/* more content */}
</div>
)
}
}export default BasicHoverExample
Now this is kind of a contrived example; regardless, managing hover state manually with DOM events is painful.
Now, let’s look at writing a basic higher-order component to manage this same hover state.
// withHoverBasicHOC.jsimport React, { Component } from 'react'const withHoverBasicHOC = BaseComponent =>
class Wrapper extends Component {
state = {
hovered: false
} this.updateHoverState = nextHoverState =>
this.setState({ hovered: nextHoverState }) render() {
return (
<div
onMouseEnter={() => this.updateHoverState(true)}
onMouseLeave={() => this.updateHoverState(false)}
>
<BaseComponent
{...this.props}
hovered={this.state.hovered}
/>
</div>
)
}
}export default withHoverBasicHOC
As you can see, withHoverBasicHOC
manages the hovered
state itself and just applies that state as a prop
to the BaseComponent
. We can use that HOC like so:
// ComposedBasicHoverExample.jsimport React from 'react'
import withHoverBasicHOC from './withHoverBasicHOC'const ComposedBasicHoverExample = props => (
<div
style={{
backgroundColor: props.hovered ? 'red' : 'blue'
}}
/>
)export default withHoverBasicHOC(ComposedBasicHoverExample)
Wow! Awesome right? What a reduction in code and state management in the component where we want a hover state!
Last but not least… let’s see how react-powerplug
can make our HOC even simpler with their Hover
component that automatically manages events for us!
// withHoverViaPowerplug.jsimport React from 'react'
import { Hover as H } from 'react-powerplug'export default Base => p => <H>{rp => <Base {...p} {...rp} />}</H>
Wut. With the Hover
components, we don’t have to manage any state ourselves which allows us to write our HOC as a pure functional component! To explain the one-liner above, we’re exporting a function that takes a component ( Base
) and the props applied to Base
( p
) and implicitly returns a Hover
component, H
which uses render props that we apply to Base
.
And from there, our integration into our affected component looks like:
// HoverExampleViaPowerplug.jsimport React from 'react'
import withHoverViaPowerplug from './withHoverViaPowerplug'const HoverExampleViaPowerplug = props => (
<div
{...props.bind}
style={{
backgroundColor: props.hovered ? 'red' : 'blue'
}}
/>
)export default withHoverViaPowerPlug(HoverExampleViaPowerplug)
Zoinks! And just like that, we have a functional one-liner to apply a hover state onto any component!