Render props

This post starts off the same as the “React Higher-order Components” section but soon changes to be Render Props focused.There's two important things to note before we get started. First, what we're going to talk about is just a pattern. It's not really even a React thing as much as it is a component architecture thing. Second, this isn't required knowledge to build a React app. You could skip this post, never learn what we're about to talk about, and still build fine React applications. However, just like building anything, the more tools you have available the better the outcome will be. If you write React apps, you'd be doing yourself a disservice by not having this in your "toolbox".

You can't get very far into studying software development before you hear the (almost cultish) mantra of **Don't Repeat Yourself** or **D.R.Y**. Sometimes it can be taken a bit too far, but for the most part, it's a worthwhile goal. In this post we're going to look at a pattern for accomplishing DRY in a React codebase, React Render Props. However before we can explore the solution, we must first fully understand the problem.

Let's say we were in charge of recreating a dashboard similar to Stripe's. As most projects go, everything goes great until the very end. Just when you think you're about to be done, you notice that the dashboard has a bunch of different tooltips that need to appear when certain elements are hovered over.

image

There are a few ways to approach this. The one you decide to go with is to detect the hover state of the individual components and from that state, show or not show the tooltip. There are three components you need to add this hover detection functionality to - **Info****TrendChart** and **DailyChart**.

Let's start with **Info**. Right now it's just a simple SVG icon.

Now we need to add functionality to it so it can detect whether it's being hovered over or not. We can use the **onMouseOver** and **onMouseOut** mouse events that come with React. The function we pass to **onMouseOver** will be invoked when the component is hovered over and the function we pass to **onMouseOut** will be invoked when the component is no longer being hovered over. To do this the React way, we'll add a **hovering** state property to our component so that we can cause a re-render when the **hovering** state changes, showing or hiding our tooltip.

Looking good. Now we need to add the same functionality to our other two components, **TrendChart** and **DailyChart**. If it's not broke, don't fix it. Our hover logic for **Info** worked great so let's use that same code again.

You probably know the next step. We can do the same thing for our final **DailyChart** component.

And with that, we're all finished. You may have written React like this before. It's not the end of the world (#shipit), but it's not very "DRY". As you saw, we're repeating the exact same hover logic in every one of our components.

At this point, the problem should be pretty clear, we want to avoid duplicating our hover logic anytime a new component needs it. So what's the solution? Well before we get to that, we need to get a refresh on two fundamental aspects of React. They are components which don't render UI and passing functions as props.

No UI Components

In most cases whenever you build a React component, the end goal is to show some UI to the screen.

View = fn(state);

However, that doesn't always need to be the case. It's entirely reasonable to have components which act as "Wrapper" components. They're responsible for handling some logic but instead of rendering their own UI, they just render another component passing it data.

In the example above **Users** is responsible for getting the users, then passing them to the **Grid** component. It doesn't have its own UI, instead, it uses the UI from the **Grid**component.

Passing functions as props

As you know, props are part of React's component API that allow you to pass data into a component.

<User id="tylermcginnis" />

Then inside of the **User** component, the **props** object would have an **id** property referencing the string **tylermcginnis**.

Now what if instead of passing a string as a prop, we passed a function?

<User id={() => "tylermcginnis"} />

Now the **props** object still has an **id** property, only now instead of being a string, it references a function. So in order to get the id, we need to invoke the function.

Now what if we wanted to pass the function prop some data? Well, it's just a function so we could do it just like we normally would by passing it an argument.


OK... but what do both of these have to do with the problem we saw earlier of duplicating our hover logic anytime a new component needs it? Well we can combine both of these simple concepts in order to solve our problem.

First, we want to create a "Wrapper" component which is responsible for managing the hover state. We'll call it, naturally, **Hover** and it'll contain all the hover logic that we had to duplicate from earlier.

The next question becomes what should **Hover** render? This is where our function prop knowledge comes into play. Let's have **Hover** receive a prop called **render**. This **render** prop is going to be a function that we can pass the **hovering** state to and it will return some UI.

Now the last change we need to make is in our **Hover**component. All we need to do is invoke **this.props.render** passing it **this.state.hover**.

Well would you look at that. Now that we have our **Hover** component, any time we need a component to be aware of its hover state, we just wrap it inside of a **Hover****render** prop.

Finally let's head back to the original code we had and see how we no longer have to duplicate all the hover logic since we have our **Hover** component.

This is what we had before.

And now with our **Hover** component, instead of each component having to duplicate the hover logic, we can wrap each one inside of the **render** prop we pass to **Hover** and then pass down the **hovering** argument as a prop.

This pattern, as you probably guessed by now, is called **Render Props**. Summarized in the React docs, "the term render prop refers to a technique for sharing code between React components using a prop whose value is a function".


Another way to utilize the render props pattern is with React's **children** prop. If you've never used **props.children** before, it's just like any other prop, except instead of you passing it explicitly to the component, React automatically does it for you and it reference whatever is in the body of the component.

In the example above, what's going to get rendered to the UI is a **div** with the words **This is props.children** inside of it.

Now what if instead of having **props.children** be a string, it was a function? Just as we saw earlier, we'd need to invoke it to get the value.

With our newly formed knowledge of **props.children**, let's update our examples from earlier. Now instead of **Hover** having a **render** prop, let's get rid of that all together and use **props.children** instead.

Looks good. Now we need to update **Hover **so instead of invoking **this.props.render**** **, it invokes **this.props.children**.

Nice. Is this better? Not really, it's just different. I prefer it, but there's nothing objectively better about it.



If you read our post about Higher Order Components, you'll be familiar with how HOCs have some pitfalls. The biggest one was with inversion of control and naming collisions. Because you have to pass your component over to the Higher-Order component, you have no control over how it's rendered. We looked at an example with React Router's **withRouter** HOC. **withRouter** will pass **match****location**, and **history** props to the wrapped component whenever it renders.

If our Game component is already receiving **match****location**, or **history** as a prop, we're going to have a naming collision and it's going to be a hard bug to track down.

Does this same pitfall occur with Render Props? Nope. Instead of handing over the component, we hand over a function. Then, when that function is invoked, it'll be passed the data we need. No inversion of control and no naming collisions since we can decide how the component is rendered.

Now the big question is, should you use Render Props or Higher Order Components? Well, that's up to you. You now know how to use them both which means you have enough information to make an informed decision for yourself.

Copyright 2023 © Borja Leiva

Made within London