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)