Thought leadership from the most innovative tech companies, all in one place.

How To Avoid Prop Drilling in React Using Component Composition

No Redux or Context API

An image of some drilling tools

Prop Drilling is the process by which you pass data from one part of the React Component tree to another by going through other parts that do not need the data but only help in passing it around.Imagine someone living in Lagos, Nigeria placing an order on Amazon for a package. The package will have to go through many hands - it has to be flown by air to Nigeria, transported to the Lagos, moved from place to place until it gets into the hands of the buyer. At each stage, Amazon employs the services of people that do not 'care' about the product, they only help to 'pass' it to the person who cared - the buyer.

Prop drilling is similar: You pass data (props) from some FirstComponent to another SecondComponent - which doesn't need the data to render but only passes it to another ThirdComponent, which also doesn't need it and may pass it to another FourthComponent. This may continue until the data gets to the ComponentNeedingProps.

Consider the code snippet below:

import React from "react";
import "./styles.css";

export default function App() {
  return (
    <div className="App">
      <FirstComponent content="Who needs me?" />
    </div>
  );
}

function FirstComponent({ content }) {
  return (
    <div>
      <h3>I am the first component</h3>;
      <SecondComponent content={content} />|
    </div>
  );
}

function SecondComponent({ content }) {
  return (
    <div>
      <h3>I am the second component</h3>;
      <ThirdComponent content={content} />
    </div>
  );
}

function ThirdComponent({ content }) {
  return (
    <div>
      <h3>I am the third component</h3>;
      <ComponentNeedingProps content={content} />
    </div>
  );
}

function ComponentNeedingProps({ content }) {
  return <h3>{content}</h3>;
}

The content prop is passed to FirstComponent in the root component, App. But FirstComponent does not need the prop to render, it only passes it to SecondComponent, which passes it to ThirdComponent, which passes it to ComponentNeedingProps. It is this component that uses the content prop to render content in <h3>{content}</h3>.

Why is Prop Drilling a Problem?

Prop drilling doesn't have to be a problem. If we are passing data only between 2 or 3 levels, we are fine. It will be easy to trace the flow of data. But imagine, we are drilling 5 levels, or 10 levels, or 15.

image

How to Solve the Problem

Prop drilling is not a new problem in React (quite obviously), and there have been many solutions that let us pass data down to deeply nested Components.

One of which is Redux: You create a data store and connect any component to the store and voila, no matter where the component is positioned in the Component Tree it has access to the store.

React also has the concept of Context which lets you create something like a global data store and any Component in 'context' can have access to the data store.

If you however want to solve this problem without using context, you can use Component Composition as suggested by the React Docs: If you only want to avoid passing some props through many levels, component composition is often a simpler solution than context.

You can learn more here Before You Use Context and also, check out Michael Jackson's thread on why you should avoid using the Context API.

How to Avoid Prop Drilling Using Component Composition

Component Composition is when you compose different Components, usually simple, together to create more complex functionality. If you have ever written a React app, I bet that you have been composing components. Take for example:

function LoginForm(props){\
 return (\
 <Input name="fname" />\
 <Button onClick={props.handleClick} />)
}

Here, by using composition we are creating a 'complex' functionality, LoginForm by composing two simpler functionalities, Button and Input components. You can read more on the composition on React documentation page.

What is the actual problem we are trying to solve?

The actual problem is that we want ComponentNeedingProps to be rendered in ThirdComponent but it needs data from the root component, App to do so. In other words, ComponentNeedingProps needs data from somewhere higher in the Component Tree (App) from where it is rendered (ThirdComponent).

The Solution?

The untamed powers of the children prop

You can compose components by making one a child of another, for example:

<ReactComponent1>
  <ReactComponent2 />
</ReactCompoent1>

ReactComponent2 is invoked inside of ReactComponent1 and hence it is a child of it. Every component has an 'automatic' prop named children that holds the children of the Component. So in ReactComponent1 we can write:

function ReactComponent1({ children }) {
  return;
  <div>
    I render my children
    {children}
  </div>;
}

How can we use it in this case? Remember we want ComponentNeedingProps to be rendered in another component down in the Component Tree, if we can pass ComponentNeedingProps as a child component with the data it needs and then render it in its parent then we have successfully avoided prop drilling.

So, we will have:

<ThirdComponent>
  <h3>I am the third component</h3>
  <ComponentNeedingProps content={content} />
</ThirdCompoent>

And in the declaration of ThirdComponent we have:

function ThirdComponent({ children }) {
  return (
    <div>
      <h3>I am the third component</h3>
      {children}
    </div>
  );
}

This doesn't look much different from what we had earlier but wait for the magic.

By following this technique of rendering children, we can refactor App to this:

function App() {
const content = "Who needs me?";
return (
    <div className="App">
    <FirstComponent>
        <SecondComponent>
        <ThirdComponent>
            <ComponentNeedingProps content={content}  />
        <ThirdComponent>
        </SecondComponent>
    </FirstComponent>
    </div>
);
}

Then we refactor each of the other components to render their children.

FirstComponent:

function FirstComponent({ children }) {
  return (
    <div>
      <h3>I am the first component</h3>;{children}
    </div>
  );
}

SecondComponent:

function SecondComponent({ children }) {
  return (
    <div>
      <h3>I am the second component</h3>;{children}
    </div>
  );
}

ThirdComponent:

function ThirdComponent({ children }) {
  return (
    <div>
      <h3>I am the third component</h3>
      {children}
    </div>
  );
}

ComponentNeedingProps stays as it is:

function ComponentNeedingProps({ content }) {
  return <h3>{content}</h3>;
}

Did you see it? We have avoided prop drilling by giving ComponentNeedingProps the data it needs right from the source of the data App and then by using the children prop, we passed it down to where it should be rendered, ThirdComponent.

Awesome. See the complete code:

When should you use the Context API?

You can also use the Context API to avoid prop drilling and I may write another article on that in the nearest future.

If you need to have some data accessible by many components at different nesting levels, then you should use the Context API. React docs advises that we 'apply it sparingly because it makes component reuse more difficult.' In other words, you might not be able to reuse your components 'out of context'.

That is it for this post, thank you for reading through. You can play with the code on CodeSandbox

Happy Coding.




Continue Learning