React's Render Props in 3 Minutes
Grasp this useful technique whilst making some watches ⌚️
What is it? permalink
A technique for sharing logic between components. Components accept a prop that returns a function responsible for rendering something. This allows our component to focus on other logic.
For those in camp TL;DR, scroll down for a demo 👍
What do they do? permalink
Handle some or all the rendering logic for a component.
<SomeDataProvider
render={data => <AwesomeComponent stuff={data.awesome} />}
/>
When to use? permalink
When you’re repeating patterns/logic across components.
Examples;
- Repeating UI structures
- Hooking into/subscribing to a data source
- Hooking into global events (scroll, resize, etc.)
A Silly Example permalink
Let’s create a watch ⌚️ Our watch component will use moment.js
, a date and time utility library.
Every 1000ms
we set the state to a new Moment
. The state change triggers a render and we display the time.
const Watch = () => {
const [date, setDate] = useState(moment())
useEffect(() => {
const TICK = setInterval(() => setDate(moment()), 1000)
return () => {
clearInterval(TICK)
}
}, [])
return (
<Strap>
<Bezel>
<Screen>
<Face>
<Value>{date.format('HH')}</Value>
<Value>{date.format('mm')}</Value>
</Face>
</Screen>
</Bezel>
</Strap>
)
}
Check out this pen by Jhey (@jh3y) on CodePen.
Don’t worry about Strap
, Bezel
, Screen
, etc. or any of the styling. We are only concerned with the technique.
But what if we wanted a watch with a different face? Many wearables allow us to change the watch face. Do we create a new Watch
variation for each face? No 👎
This is where a render
prop comes into play. We can adjust Watch
to utilise one for rendering a watch face. Watch
becomes a component that provides the current time and passes that to a render
prop.
const Watch = ({face}) => {
const [date, setDate] = useState(moment())
useEffect(() => {
const TICK = setInterval(() => setDate(moment()), 1000)
return () => {
clearInterval(TICK)
}
}, [])
return (
<Strap>
<Bezel>
<Screen>
{face(date)}
</Screen>
</Bezel>
</Strap>
)
}
Now we can create stateless face components that take a Moment
and render it in different ways.
Extracting our initial implementation might look something like
const CustomFace = date => (
<Face>
<Value>{date.format('HH')}</Value>
<Value>{date.format('mm')}</Value>
</Face>
)
// JSX to render being <Watch face={CustomFace} />
Check out this pen by Jhey (@jh3y) on CodePen.
What if we don’t pass in face
? We’d get a blank watch. But we could rename CustomFace
to DefaultFace
and make it a defaultProp
on Watch
👍
Check out this pen by Jhey (@jh3y) on CodePen.
Nice 👍
Let’s create a new face. An analog one 🕔
const AnalogFace = date => {
const seconds = (360 / 60) * date.seconds()
const minutes = (360 / 60) * date.minutes()
const hours = (360 / 12) * date.format('h')
return (
<Face>
<Hand type='seconds' value={seconds}/>
<Hand type='minutes' value={minutes}/>
<Hand value={hours}/>
</Face>
)
}
This one takes the date and displays it with hands ✋
Check out this pen by Jhey (@jh3y) on CodePen.
We could then extend this to create a slew of different watch faces 🤓 No need to repeat the logic.
const App = () => (
<Fragment>
<Watch face={DayFace} />
<Watch />
<Watch face={AnalogFace} />
<Watch face={DateFace} />
<Watch face={SecondsFace} />
</Fragment>
)
render(<App />, ROOT)
Giving us
Check out this pen by Jhey (@jh3y) on CodePen.
And that’s it!
Using a render
prop on our Watch
component keeps the logic in one place and stops us from repeating ourselves. This makes things easier to maintain and reuse 💪
DOs 👍 permalink
- Use when there’s an opportunity to share component/render logic
DON’Ts 👎 permalink
- Overuse. Another pattern may be more appropriate.
- Avoid implementing
render
props withPureComponent
s unless your prop is statically defined
NOTES ⚠️ permalink
- A
render
prop can have any name.children
is arender
prop. - Most components using a
render
prop could also be a higher-order component and vice versa!
That’s it! permalink
A 3-minute intro to render
props!
For further reading, check out the React Docs.
All the demos are available in this CodePen collection.