Render props is a relatively new and popular way to share/reuse code in React. React router and Downshift are libraries that use render props.

What is a render prop? It is basically a prop with a function as its value. This function prop informs the component of what to render. From the React docs, a component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic.

Now that we know what render props are let's see it in action.

Reuse code using render props

Essentially, what we want to be able to do is share state/behavior of an existing component to other components that need the same state.

So for example, we have a simple app that tracks the mouse movement on a screen:

import React from 'react'
import ReactDOM from 'react-dom'

const App = React.createClass({
  state = { x: 0, y: 0 }

  handleMouseMove = (event) => {
    this.setState({
      x: event.clientX,
      y: event.clientY
    })
  },

  render() {
    const { x, y } = this.state

    // As the mouse moves around the scree, the component displays the x, y coordinates
    return (
      <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
        <h1>The mouse position is ({x}, {y})</h1>
      </div>
    )
  }
})

ReactDOM.render(<App/>, document.getElementById('app'))

If we wanted to share this behavior to another component, how would we use render props to wrap this behavior?

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'

class Mouse extends React.Component {
  static propTypes = {
    render: PropTypes.func.isRequired
  }

  state = { x: 0, y: 0 }

  handleMouseMove = (event) => {
    this.setState({
      x: event.clientX,
      y: event.clientY
    })
  }

  render() {
    return (
      <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    )
  }
}

class App extends React.Component {
  render() {
    return (
      <div style={{ height: '100%' }}>
        <Mouse render={({ x, y }) => (
          <h1>The mouse position is ({x}, {y})</h1>
        )}/>
      </div>
    )
  }
}

ReactDOM.render(<App/>, document.getElementById('app'))

What we've done is create a new component <Mouse> that takes a render prop whose value is a function. The prop is then used inside <Mouse>'s render to determine what to render.

Note: The <Mouse> state is exposed in <App> component using the render prop. This allows for component App to render whatever it wants with that state.

The render prop in <Mouse> allows it to dynamically determine what to render; therefore, it is totally portable and we can use it for many different use cases we might have with the mouse position.

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import cat from "./cat.png";

class Mouse extends React.Component {
  static propTypes = {
    render: PropTypes.func.isRequired
  }

  state = { x: 0, y: 0 }

  handleMouseMove = (event) => {
    this.setState({
      x: event.clientX,
      y: event.clientY
    })
  }

  render() {
    console.log("mouse", this.state)
    return (
      <div onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    )
  }
}

class Cat extends React.Component {
  render() {
    const {x, y} = this.props.mouse;

    return (
      <img src={cat} style={{ position: 'absolute', left: x, top: y, height: 50 }} />
    );
  }
}

class App extends React.Component {
  render() {
    return (
      <div>
        <div>
          <Mouse render={({ x, y }) => (
            <h1>The mouse position is ({x}, {y})</h1>
          )}/>
        </div>

        // different use case for the mouse position
        <div>
          <Mouse render={({ x, y }) => (
            <Cat mouse={{ x, y }} />
          )}/>
        </div>
      </div>
    )
  }
}

export default App;

We just added a new component <Cat> which uses the mouse position behavior to move a cat around our screen!

Remember that we do not have to use a prop named render. As long as we use a prop whose value is a function, that the component uses to determine what to render, we are using the render prop pattern. This is to say, we can use the children prop:

<Mouse children={({ x, y }) => (
  <p>The mouse position is {x}, {y}</p>
)}/>

This is commonly known as the children as a function concept and it is the exact same thing as render prop. react-motion uses this concept.

Conclusion

That is it! Render props makes reusing code really simple. Remember every time we create components we want them to be as reusable as possible. So make sure you explore more on how to make your components portable. This talk by Michael Jackson is very resourceful and he explains why render props is a better technique over using higher order components (HOCs).

Happy coding everyone!