Skip to main content

Command Palette

Search for a command to run...

Prop Drilling in React and how to avoid it

Updated
3 min read
Prop Drilling in React and how to avoid it

Introduction :

There will be cases where we want to pass some data from one component to another component in React.

So, Before diving into prop drilling, lets first see how we can send data from one component to another. We can do it using props.

import React from "react";
import {Child} from "./Child";

function Parent(){
const data = "Hello";
  return (
      <>
       <Child data={data} />
      </>
  )
}
import React from "react";

function Child(props){
  return (
      <>
       <p> Child </p>
       <p> Data from parent is {props.data} </p>
      </>
  )
}

In the above example, we want to send data from Parent component to Child component and that data is passed from Parent to Child component, as a prop.

Prop Drilling :

Now, what if we want to send data from parent to grandchild, how can we do it ?

Let's see

import React from "react";
import {Child} from "./Child";

function Parent(){
const data = "Hello";
  return (
      <>
       <Child data={data} />
      </>
  )
}
import React from "react";
import {GrandChild} from "./GrandChild";

function Child(props){
  return (
      <>
       <p> Child </p>
       <GrandChild data={props.data} />
      </>
  )
}
function GrandChild(props){
  return (
      <>
       <p> GrandChild </p>
       <p> Data from parent is {props.data} </p>
      </>
  )
}

As you can see in above code, to pass data from Parent to Grandchild, we had to pass the data to Child even though it doesn't need that data, just so that it can pass it to GrandChild.

Here only Child component has got the data even though it doesn't need, what if there are many components like that which get props data just so that they can pass props data to descendants ? This is called Prop Drilling.

We can avoid Prop Drilling by using useContext.

useContext :

Let's see it with an example,

context.js

import { createContext } from "react";

const DataContext = createContext({ text: "Hi" });

export { DataContext };

In this example, we want to pass data present at top level (App component) to bottom level (Child3 component), So we can use createContext with or without a default value (that we want to pass to bottom level) given as argument.

App.js

import "./styles.css";
import { useContext } from "react";
import { DataContext } from "./context";

export default function App() {
  return (
    <DataContext.Provider value={{ text: "Hello" }}> 
      {/*in the above line we are passing value as an object 
     and any child can access data using useContext*/}
      <div className="App">
        <div>App</div>
        <Child1 />
      </div>
    </DataContext.Provider>
  );
}

function Child1() {
  return (
    <>
      <div>Child1</div>
      <Child2 />
    </>
  );
};

function Child2() {
  return (
    <>
      <div>Child2</div>
      <Child3 />
    </>
  );
};

function Child3() {
  const { text } = useContext(DataContext);
  {/* We can access the value that is passed at the top level by
  using useContext with the created context as argument */}

  return (
    <>
      <div>Child3</div>
      <div>Data is {text}</div>
    </>
  );
};

As you can see in the above example,

<DataContext.Provider value={{ text: "Hello" }}>  
  {children to be provided here} 
<DataContext.Provider/>

DataContext is used to wrap the children so that they can access the value provided as shown above.

const { text } = useContext(DataContext);

Child Components can access that value using useContext as shown above.

Conclusion :

Here we can see that, data is not passed to each of the children, instead data is directly used by children using useContext. In this way we can avoid Prop Drilling.