Using Composition and Render Props instead of Context API

People used to jump to using Redux when they need to pass props more than a few levels deep. After discovering React’s new Context API, lots of people started using it instead of Redux when they just need to pass some props down. But if what you are trying to achieve is to just pass down some props without dealing with passing it to every component in between, you don’t have to use Redux or Context API.

I use this method often and I wanted to make a blog post about it after seeing Dan Abramov’s tweet:

I will try to explain how to pass our props to deep levels without passing it to every component in between using Composition and Render Props. It took me a while to learn these topics but when I understood them, I noticed that they are not complicated at all.

In this post, example components will be extremely simple and I will create all components in the same file to make it easier to see what’s going on.

I created a React app on codesandbox.io and you can see the final version in the link below:

https://codesandbox.io/s/0mo9k1xjzn

Component Structure

For this example, I created four nested components namely Main, First, Second and Third. Our final component structure should look like this:

Let’s imagine Main has a state which we want to pass to Third. With prop drilling, we can keep passing our prop down until we reach the component that we are going to use it.

Prop Drilling

If we are using prop drilling, our components will look like this:

I created WithPropDrilling.js file with the code above

So the Main component has the state and we want to pass it to the Third component. Since we only have three components, this solution is completely fine. However, things get complicated if we need to go much deeper than this. But for simplicity, I won’t add any more components.

As expected, if I mount my component to the app, it should work. Let’s test it:

So far there is nothing new. But let’s try something else now.

Composition

I created another file, Composition.js and copied the code from WithPropDrilling.js. This time, I want to reshape the Main component. Let’s try something like this:

Now, it looks like we found a way to pass the prop directly to the Third component. Let’s do a test drive:

Looks like we weren’t successful at passing the prop to the Third component. Could the structure we provided for the Main component be incorrect? Let’s add another element and test if we can see it:

It’s looking like our app doesn’t care about the structure we created in the Main component. Actually, we passed children elements to our components in the Main component but we never used them. Instead, we statically used our component names as children. So components have nothing to do with the children we passed.

Right now, we are explicitly declaring which components will be children to the First and Second components. What would happen if the Main component was deciding what other components will be rendered as children? Will this change anything?

Since we already created the component structure in our Main component, there is no need to use component names statically in our children components. Instead, we can use props.children. Let’s make this little change:

Child component names in First and Second components changed with {props.children}

Now the components First and Second doesn’t know which other components are their children. We took Now the components First and Second doesn’t know which other components are their children. We took this responsibility from our Second and Third components. We created our structure in the Main component and we are deciding what our Second and Third components will have as children in the Main component.

Let’s check again after the changes:

We can now see the text passed as a prop along with the h2 element which is the sibling element of our Third component. This means we could successfully pass our props to deep levels with Composition.

Render Props

So far we could manage to pass components down without using Prop Drilling, Context API or Redux. But there is one other way to create a similar behavior. It’s called Render Props.

First I recommend you to read the official docs: https://reactjs.org/docs/render-props.html

Render Props are just props carrying a function which returns JSX. In simpler terms, according to the official docs, “a render prop is a function prop that a component uses to know what to render.

The name “render” is just for naming convention, you can give any name to this prop but I will stick to using render.

I created a copy of Composition.js and renamed to WithRenderProps.js. With a few small differences, we can get the same functionality again.

This time in the Main component, instead of placing Second and Third components as a descent of the First component, we will create a prop named render. This prop will be an arrow function which will return what to render in the component. Now it is just like using children, but we are returning our children components in our render prop, not placing them directly under the component.

And since component First doesn’t have any children prop now, we need to make a change there too.

There is a small difference here. When we were using composition, we were using {props.children}. But this time it’s {props.render()}. Since our render prop is a function, we need to invoke the function to get what to render.

So, why might we need this approach? What kind of extra benefits render props could provide us? The answer is simple, passing parameters. To show an example, let’s create a random number in component First. Then pass this number as a parameter to the render function.

Let’s add a place to show this number in the Main component.

Now we should see a random number between 0–9 on every refresh. Actually refresh is not necessary, a state change will trigger components to re-render. We could also use the forceUpdate method on the component Main but I want to give more visible examples. To see this let’s add ‘counter’ to our state, ‘increaseCounter’ method and elements to display our values.

Now every time when we click on the button it will increase the counter. This change will cause a re-render and we will see a new random number.

Now we can use Render Props to pass props down to deep levels, plus we can send parameters back.

Conclusion

In this post, I tried to explain Composition and Render Props with easy examples. I want to give one more example of Render Props and add some final words. But since I’m working on a blog script lately, I want to publish the rest of this post in my blog. You can read the rest of this post in the link below

https://blog.barisozcetin.me/using-composition-and-render-props-instead-context-api/