Render Props and Higher Order Components
K
Posted on May 30, 2018
Cover image by Graffiti Picture Taker, on Flickr
Last week I entered the first ever dev.to contest and submitted a serverless multiplayer clicker game.
It would be awesome to get your โค๏ธ & ๐ฆ on my entry-post
I'd also like to give you some know-how in return.
If you're a React developer and followed the ecosystem for a few years, you probably noticed the advent of render props (RP), or function as children, lately.
All the time people were telling you about higher order components (HoC) and now this?
Well I was confused too, but if you take React programming practices into account, you'll see that RPs make totally sense.
What
Render props are simply props that will be used in a render call somehow. They take a function that needs to return an element. This function also gets some dynamic data via its arguments, these can be used by the returned elements.
They are the dependency injection alternative to HoC.
Why
Every time you create an element from your RP based component, you can pass different elements into its RP. A HoC will wrap your component at definition time and not at render time.
In the last years it became common in React coding practice to use dependency injection to create nested elements, RPs are a natural extension of that principle.
For example, you wouldn't define a List
component like this:
const List = props => <ul>{props.data.map(i => <ListItem text={i}/>)}</ul>;
const ListItem = props => <li>{props.text}</li>;
// usage
<List data={["hello", "world"]}/>
Because now your List
needs to know about the data
and which ListItem
it needs to render.
Instead you would define it like this:
const List = props => <ul>{props.children}</ul>;
const ListItem = props => <li>{props.text}</li>;
// usage
<List>
{data.map(i => <ListItem text={i}/>)}
</List>
Because now you can inject the data
and child components into the List
and it just has to render it. You could, for example throw in another ActiveListItem
the List
doesn't need to know anything about.
Components with RPs play really nicely with this. Just imagine, your List
would simply render all its children
and pass them some data it gathered.
Higher Order Fetch
HoC are another way to do this, but the idea behind them is to create a wrapped
component you can use everywhere that has some extra abbilities.
A fetch as HoC could look like this
const wrapWithFetch = Wrapped => class Fetch extends React.Component {
state = { result: null };
componentDidMount() {
fetch(this.props.url)
.then(r => r.json())
.then(result => this.setState({result}))
}
render() {
const {result} = this.state;
return result? <Wrapped data={result}/> : null;
}
}
// Stateless component that displays text
const Text = ({data})=> <p>{data.name}</p>;
// Wrappted text that gets data
const FetchText = wrapWithFetch(Text);
// Usage
<FetchText url="/user/123"/>
Render Prop Fetch
The RP version could look like this:
class Fetch extends React.Component {
state = { result: null };
componentDidMount() {
fetch(this.props.url)
.then(r => r.json())
.then(result => this.setState({result}))
}
render() {
const {result} = this.state;
return result? this.props.render(result) : null;
}
}
// usage
<Fetch url="/user/123" render={user => <p>{user.name}</p>}/>
When it's mounted it will fetch some data and pass it to the RP.
Since children
are props, you could also use them instead of a custom prop.
<Fetch url="/user/123">{user =>
<p>{user.name}</p>
}</Fetch>
Which would lead to a Fetch
component that looks like that:
class Fetch extends React.Component {
state = { result: null };
componentDidMount() {
fetch(this.props.url)
.then(r => r.json())
.then(result => this.setState({result}))
}
render() {
const {result} = this.state;
return result? this.props.children(result) : null;
}
}
As you can imagine, now you can simply wrap any children into a function that will receive data from the server and only be rendered when the data is available.
Conclusion
Render Props can be used to add even more dependency injection into your app, making it much more flexible to change.
You can add new elements into the RP to simply change what is displayed, for example change tables to Graphs etc.
But you can also change the wrapping RP component so the children will now receive data from a different source, but you wouldn't have to change the children, because you could map the data from the RP arguments to the right child-props on-the-fly.
Contest
Also, if you liked this post:
I would appreciate your โค๏ธ & ๐ฆ on my entry-post
Posted on May 30, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 28, 2024
November 27, 2024