Grasp this useful technique whilst making some watches ⌚️
What is it?
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 render props do?
Handle some or all the rendering logic for a component.
When to use?
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
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.
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} />
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
👍
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 ✋
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
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 👍
- Use when there’s an opportunity to share component/render logic
DON’Ts 👎
- Overuse. Another pattern may be more appropriate.
- Avoid implementing
render
props withPureComponent
s unless your prop is statically defined
NOTES ⚠️
- 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!
A 3-minute intro to render
props!
For further reading, check out the React Docs.
All the demos are available in this CodePen collection.
As always, any questions or suggestions, please feel free to leave a response or tweet me 🐦!
As always, any questions, please feel free to leave a response or tweet me 🐦! And say "Hey!" anyway 😎
Top comments (0)