DEV Community

Cover image for Render Props and Higher Order Components
K
K

Posted on

Render Props and Higher Order Components

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"]}/>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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"/>
Enter fullscreen mode Exit fullscreen mode

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>}/>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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

Top comments (4)

Collapse
 
kepta profile image
Kushan Joshi • Edited

Great article K, really contrasts HOC and render props.
Just a minor concern, I am not sure whether the first example you posted qualifies as a render props.

<List>
  {data.map(i => <ListItem text={i}/>)}
</List>

As you said a render prop is a function which the child component invokes to figure out how to render dynamically. However in the example above you are not passing any function to child component, instead you are passing a simple array of react elements.
Cheers 🥂

Collapse
 
kayis profile image
K

Thanks!

This wasn't an example of a render prop but of general dependency injection.

I tried to show how DI leads to RP. :)

"you said a render prop is a function which the child component invokes"

No no, the children will be returned by this function, the parent invokes it, because the parent gets it passed via the render prop. :)

Collapse
 
kepta profile image
Kushan Joshi

Gotcha! Thanks for clarification.

Collapse
 
lucciddev profile image
lucciddev

Awesome post bro. I loved the way you dissected the topic. But is it a bad practice to use HOC instead of RP or can you use either based on the problem you are trying to solve? Sometimes I have component eg a page that is set to receive certain props when wrapped with a HOC so I just need to declare the page wrapped with the HOC as a component to be displayed for a certain route in my application. With this kind of example I don't think I need render props or what do you think? Would appreciate your view on this.