React's Render Props Technique in 3 Minutes

jh3y

Jhey Tompkins

Posted on May 22, 2020

React's Render Props Technique in 3 Minutes

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.

<SomeDataProvider
  render={data => <AwesomeComponent stuff={data.awesome} />}
/>
Enter fullscreen mode Exit fullscreen mode
Example component using render props

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.

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>
  )
}
Enter fullscreen mode Exit fullscreen mode
First watch implementation

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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} />
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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

NOTES ⚠️

  • 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!

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 😎

💖 💪 🙅 🚩
jh3y
Jhey Tompkins

Posted on May 22, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related