DEV Community

Danijel Maksimovic
Danijel Maksimovic

Posted on

Render props pattern in React

Render props is a pattern popularized by Michael Jackson of reacttraining.com and Kent C. Dodds of Paypal among others. We can see this pattern being used more and more in React itself and it's ecosystem (Context API, React Router, Apollo Client, react-motion, Downshift etc.).

When would you use this pattern?

This pattern should be used when you are building a component that it's not concerned with rendering but it has some important business logic to do for your app. In this way you can reuse this component for business logic (fetching data, doing calculations etc.).

Example

// component that implements render prop
import React from 'react';
import PropTypes from 'prop-types';

import { fetchPosts } from './api';

class FetchPosts extends React.Component {
  static propTypes = {
    children: PropTypes.func
  };

  state = { articles: [], loading: false };

  async componentDidMount() {
    this.setState({ loading: true });

    const articles = await fetchPosts();

    this.setState({ articles, loading: false });  
  }

  render() {
    return this.props.children({articles: this.state.articles});
  }
}

// component that is using render prop component
import React from 'react';

import FetchPosts from './FetchPosts';

const ArticleList = props => {
  return (
    <FetchPosts>
      {({ loading, articles }) => {
        if (loading) {
          return 'Loading...';
        }

        return articles.map(article => <ArticleListItem article />);
      }}
    </FetchPosts>
  );
};

In our example we have a component that is used only for fetching data and it leaves rendering to children passed as a prop (hence the name render props). ArticleList component is using that data to render a list of articles. We could have a different component to render a grid list of articles that gets data from our FetchPosts component.

Example from the community

React's Context API

Let's take a look at React's context API example:

import React from 'react';

const state = {
  counter: 0

const { Provider, Consumer } = React.createContext(state);

const App = props => {
  return (
    <Provider value={state}>
      <Counter />
    </Provider>
  );
}

export default App;
export { Consumer };

// Counter.js
import React from 'react';

import {Consumer} from './App';

const Counter = props => {
  return (
    // render props used here
    <Consumer>
      {state => (
        <p>{state.counter}</p>
      )}
    </Consumer>
  );
}

In our App component we are creating new context with React.createContext and we are passing default value for our context. Provider component is used to set the value which will be provided to a component that requests that value.

In our Counter component we are using Consumer component that was created as a result of createContext's invocation and that component is implementing render props pattern. Children of Consumer component is a function which is called with value that is provided as a prop to Provider component.

React router Route component

React router is one of the most commonly used packages from React community. It also implements render props pattern (it's not using children as a function but it's using render prop). Example incoming:

import React from 'react';
import {BrowserRouter as Router, Route} from 'react-router-dom';

const App = props => {
  return (
    <Router>
      <Route path='/posts' render={(props) => (// ...do something with props and render to UI)} />
    </Router>
  );
}

As you can see Route component accepts render props which is a function which gets props (history, location, match etc.)

Apollo client's Query component

Apollo client implementation for React uses render props in it's Query component. Example of usage:

import React from 'react';
import {Query} from 'react-apollo';

const App = props => {
  return (
    <Query query={POSTS_QUERY} variables={variables}>
      {({data, loading, error, fetchMore}) => {
        if (loading) {
          return 'Loading...';
        }

        if (error) {
          return error;
        }

        return <ArticleList data={data} />
      }}
    </Query>
  );
};

This example is a bit more complex regarding data that's returned from render prop. When Query component is rendering it's children it's passing an object that has decent number of properties that we can use. It's a general rule that your function accepts only one parameter and that you are destructuring properties when implementing function. But you don't have to do it, it's still a function and you can implement it however you want it.

Conclusion

In my opinion and experience using React every day I find that render props is greatly used pattern that suites React's component composition pattern perfectly. I encourage you to think about how you can use this pattern in your code to make it more readable and reusable. Also, please share your usages of this pattern in the comments 👇 Thanks for reading 😄

Top comments (0)