Refactoring a render prop with hooks
Just a few weeks ago, I published a guide on creating render props components. Today, at React Conf 2018, the React team announced “Hooks”, a new feature proposal in React v16.7.0-alpha, that will change the way we think about components.
Hooks serve as a method for abstracting state-based logic and lifecycle methods into reusable functions. These hooks replace a number of the most common use cases for render props.
So let’s look at how to convert a simple render prop component into a hook!
The old: render prop
In my previous article, we created a render prop component called “Open” that kept track of an open/close state and shared a method to change it. Here’s the code:
import React from 'react'export default class Open extends React.Component {
state = { isOpen: false }toggle = () => this.setState({ isOpen: !this.state.isOpen })render() {
const renderProps = {
isOpen: this.state.isOpen,
toggle: this.toggle
}return typeof this.props.children === 'function'
? this.props.children(renderProps)
: this.props.children
}
}
Open
could then be implemented in typical render prop fashion:
export const App = () => (
<Open>
{openProps => (
<div className="app">
{openProps.isOpen
&& <NewMenu toggle={openProps.toggle} />}
<main>
<button
onClick={openProps.toggle}
type="button"
>
MENU
</button>
<Welcome />
</main>
</div>
)}
</Open>
)
Not bad, but let’s refactor this with hooks.
The new: hook
React’s Hook API allows us to implement state on purely functional components. Let’s create it one step at a time:
1. useState
Before we get to that, we’re going to name our function useOpen
. Prefacing hooks with use
is a convention the React team prefers. Now, we’ll import useState
function from React:
import { useState } from 'react'function useOpen() {
const [isOpen, setIsOpen] = useState(false)
// isOpen is initialized to the passed value: false
// setIsOpen is a function that can update the value return { isOpen, setIsOpen }
}
We call useState
and pass it an initial value. This function returns an array with the value and a function for updating that value. To access these data, we’ll create variables by destructuring the array returned by useState
.
At this point, isOpen
is assigned to false
and setIsOpen
is a function that is waiting to update that value. Finally, we return those variables in an object.
2. Create “method”
Our render prop had one method to toggle isOpen
between true and false. We can implement the same functionality, but with a basic function:
import { useState } from 'react'function useOpen() {
const [isOpen, setIsOpen] = useState(false)
function toggle {
setIsOpen(!isOpen)
} return { isOpen, toggle }
}
The toggle
function calls the set
function provided by useState
and provides the opposite value of isOpen
. Then, we return toggle
instead of setIsOpen
.
Now we can set it all up!
3. Implement useOpen
First, we have to remove all the references to the Open
and all associated nesting in our App
component. Then, we’ll:
- Import
useOpen
- Create our variables by calling
useOpen
- Remove all references to
openProps
Here’s what we’re left with:
import useOpen from './hooks/useOpen'export const App = () => {
const { isOpen, toggle } = useOpen()
return (
<div className="app">
{isOpen
&& <NewMenu toggle={toggle} />}
<main>
<button
onClick={toggle}
type="button"
>
MENU
</button>
<Welcome />
</main>
</div>
)
}
No more parent components, passed functions, or faux-nesting. Just grabbing variables from a function and referencing them in the return of your component.
That’s it! We’ve recreated a basic render prop component as a hook.
Summary
Hooks are a “new feature proposal” and therefore are not ready for implementation in an app. That being said, the React team sees hooks and class-less components as the future of the library. Read here for the motivation behind this feature and the future direction of React.
While the syntax of hooks are less intuitive than render props, the implementation is more straightforward.
For a more detailed explanation of hooks, checkout the official docs (and Dan Abramov’s and Ryan Florence’s talks at React Conf 2018).
Hopefully that helped! Let me know what you think about React’s new hooks in the comments below.
Happy coding!