The Render Props pattern is one of the most known React component patterns. It is useful in scenarios where we need to inject dependencies (props) in our components.
What does it solve
We can decouple a provider / logic component from a presentation component.
If I have a
Layout
component that renders a
HomePage
or
ProfilePage
component, I don't want to have it dependent on
Page
components. So the
Layout
component doesn't import
HomePage
or
ProfilePage
, but the
Layout
can still pass props to them.
Render props in action
So I can have a
Layout
component that can render any other component we want. Something like:
<Layout>
<HomePage/>
</Layout>
// or
<Layout>
<ProfilePage/>
</Layout>
// etc
So in this case, Layout is implemented to render
children
- this way it's able to render any other component inside it:
function Layout({ children }) {
const [isOpen, setIsOpen] = React.useState(false);
return (
<div className="layout">
<button onClick={() => setIsOpen(!isOpen)}> Menu </button>
{isOpen && <Menu />}
{children}
</div>
);
}
Ok, but we have a problem - the
HomePage
and
ProfilePage
components also need to know if the menu is open - for some reason. How do we do that?
function Home({ isOpen }) {
return (
<div className="home">
<h1> Home</h1>
{!isOpen && <button> Contact! </button>}
</div>
);
}
At this point, you are encouraged to spend a few minutes and think about a solution before you scroll down.
Some not so good approaches:
โ put isOpen in Redux (it's not really global state)
โ use a Context Provider
โ use conditional rendering to directly render the
Page
components in
Layout
โ The solution
Obviously, refactor our code to use the Render Props pattern.
function Layout({ children }) {
const [isOpen, setIsOpen] = React.useState(false);
return (
<div className="layout">
<button onClick={() => setIsOpen(!isOpen)}> Menu </button>
{isOpen && <Menu />}
{children(isOpen)}
</div>
);
}
Now when we wrap our components in Layout it looks like this:
<Layout>
{(isOpen) => <Home isOpen={isOpen} />}
</Layout>
// or
<Layout>
{(isOpen) => <ProfilePage isOpen={isOpen} />}
</Layout>
We changed two things:
- We call
children
as a function when we render it inLayout
and pass the desired props - When we render something wrapped in the Layout component - we use the function syntax to render the child component
A render prop is a function prop that a component uses to know what to render (React docs).
Disadvantages of Render Props
Now it's cool to use design patterns, but we need to keep in mind - all design patterns have also disadvantages.
The disadvantage of Render props is nesting - if we overuse it, for example:
<Layout>
{(isOpen) =>
<Home isOpen={isOpen} >
{(handleSubmit) => <ContactForm submit={handleSubmit}/>}
</Home>}
</Layout>
So it is advisable to have it only in one layer, and only when it's actually needed.
Conclusion
Render props is an interesting pattern because we can inject props into components while having our provider component generic - only rendering children.
It's important to know that there are limited scenarios today to use render props, like the one I described above. Some use cases of render props can be refactored to React hooks. So try to consider hooks before trying RP.
๐ Comment below ๐ What are your thoughts on the render props pattern or other React patterns? Do you still use Render props or other older React patterns? Or you are only using React Hooks?
Leave a ๐งก & ๐ฆ. For more interesting content also check out my Twitter.