ReactJS: prop-drilling and how to work-around it with useContext hook
Published: 2024-04-09 15:33:51

A traditional ReactJS application is built using components which represent a part of UI on the application. A component facilitates reusability of data in an application. When we have multiple components, we may have to use data like variables and functions repeatedly across these components. This calls for passing of such reusable data from one component to other and this is where "props" come into play.

Let's look at a very simple example:

```
function Greeting(props) {
  return <h1>Welcome to the city, {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Greeting name="Denis" />
      <Greeting name="Chris" />
      <Greeting name="Rajni" />
      <Greeting name="Deva" />
    </div>
  );
}
```
In the above example, a greeting text can be repeated for 4 different instances/names, using the props for name variable. The App function/component's variables are passed into the Greeting function/component using props.


## Prop-Drilling
Passing data from one component to another has its own rules. Usually data can be passed from a parent to a child component using props. In most of the applications, these props may have to be passed from parent component to down the hierarchy across several child and grandchild components and so on. This results in a large number of nested components. 

By default, props can be passed from a parent component only to the immediate next child component in the hierarchy. Suppose a child/grandchild component somewhere far down the line requires the props from the parent component, the props can't be passed directly but only by going through other child components in between, even if those middle child components don't require the props. This phenomenon is called prop-drilling which is obviously not the best coding practice.

![Prop-drilling in ReactJS (source:react.dev docs)](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bur58s1u0hfd3bc6avtu.png)
_Prop-drilling in ReactJS (source:react.dev docs)_

## useContext

There's a workaround for this prop-drilling in ReactJS. Most times, there may be a need for passing props from the parent component to some distant child component and not to the components in between. This is possible through the Context API in React, by using a hook called _useContext_. 

In order to deploy Context for components, we need to first Create the Context using _createContext_, then wrap the child components using _Context Provider_ and finally use the _useContext_ hook from react library. Let's look at the structure of useContext step by step through a simple example where the DOM displays the game level in which a Player is:

1. First import the necessary hooks from react library
```
import { useState, createContext, useContext } from "react"
```
2. Then create a Context for the Level using _createContext_
```
const LevelContext = createContext()
```
3. Create the first Component for displaying a message when user is at Level 01. The first level is initiated using useState.
```
function Component1() {
  const [level, setLevel] = useState("Level 01")
  return (
    <LevelContext.Provider value={level}>
      <h1>{`You are in ${level}!`}</h1>
      <Component2 />
    </UserContext.Provider>
  );
}
```
4. Create rest of the components by hardcoding the level except for the last one which will use Context to take props from Level 01 parent component.
```
function Component2() {
  return (
    <>
      <h1>Level 02</h1>
      <Component3 />
    </>
  );
}
function Component3() {
  return (
    <>
      <h1>Level 03</h1>
      <Component4 />
    </>
  );
}
```
5. Finally the Component 4 will use props from Component 1 directly without involving the components in between

```
function Component4() {
  const level = useContext(LevelContext);

  return (
    <>
      <h1>Component 4</h1>
      <h1>{`You are at ${level} again!`}</h1>
    </>
  );
}
```

Here is the whole code altogether:

```
import { useState, createContext, useContext } from "react"

const LevelContext = createContext()

function Component1() {
  const [level, setLevel] = useState("Level 01")

  return (
    <LevelContext.Provider value={level}>
      <h1>{`You are in ${level}!`}</h1>
      <Component2 />
    </UserContext.Provider>
  );
}

function Component2() {
  return (
    <>
      <h1>Level 02</h1>
      <Component3 />
    </>
  );
}

function Component3() {
  return (
    <>
      <h1>Level 03</h1>
      <Component4 />
    </>
  );
}

function Component4() {
  const level = useContext(LevelContext);

  return (
    <>
      <h1>Component 4</h1>
      <h1>{`You are at ${level} again!`}</h1>
    </>
  );
}
```

The output for this is:

`You are at Level 01!
Level 02
Level 03
You are at Level 01 again!
`