React's Render Props in 3 Minutes

May 22, 2020
React, Javascript
~3min
Share on Twitter

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.


            js
            
          <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.


            js
            
          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.


            js
            
          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 🕔


            js
            
          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.


            js
            
          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 with PureComponents unless your prop is statically defined

NOTES ⚠️ permalink

  • A render prop can have any name. children is a render 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.