DEV Community

Cover image for Using React Context to Prevent Prop Drilling
Shoki Ishii
Shoki Ishii

Posted on • Edited on

4

Using React Context to Prevent Prop Drilling

In this article, I will summarize the usefulness and usage of React Context, which is useful for prop in React.

Overview

Problem

If you don’t use React Context and try to pass data to prop, you need to pass the data to prop and receive it again and again. And you won’t know what you are passing if the components are too nested.
For example, as shown in the figure below, when you want to pass the array data ‘languages’ in the green background to container 3, you can implement it in two patterns: with using React Context and without using React Context.

Pattern 1 (with using React Context)

with using React Context

Pattern 2 (without using React Context)

without using React Context

In the case of Pattern 2, where the React Context is not used, the data is passed in the following order: Background → Container1 → Container2 → Container3. Even though the data is only used in Container3, Prop is being passed again and again, called prop drilling.

The code for this looks like the following.

import "./App.css";

function App() {
  const languages = ["JavaScript", "Python", "Java", "Golang"];
  return (
    <div className="background">
      Background 
      <p className="languages">
        Pass languages[JavaScript, Python, Java, Golang] to Container 3
      </p>
      {/* languages ->  Container1*/}
      <Container1 languages={languages} />
    </div>
  );
}

export default App;

function Container1({ languages }) {
  return (
    <div className="container1">
      Container 1
      <Container2 languages={languages} />
      {/* Container1 ->  Container2 */}
    </div>
  );
}

function Container2({ languages }) {
  return (
    <div className="container2">
      Container 2
      <Container3 languages={languages} />
      {/* Container2 ->  Container3 */}
    </div>
  );
}

function Container3({ languages }) {
  return (
    <div className="container3">
      Container 3
      <ul className="languages-area">
        {languages.map((language, i) => (
          <li key={i}>{language}</li>
        ))}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Container1 and Container2 receive data and carry the data to Container3, even though they do not actually use the data.
This is not impossible to do, but if there are 10 layers of components, it will no longer be clear what data is being passed or received.

Solution

So you will use React Context to pass data directly from Background to Component3. It’s easy to use, and here’s an image of React Context.
React Context Image

Create a friend Mr. B who is a mutual friend of Mr. A and Mr. C, and pass and receive data through Mr. B.

Implementation

Step 1. import React Context

First, import the react context.
Then, use createContext() to declare a context called LanguageContext.
↑ The one created here is the so-called common friend Mr. B of Mr. A and Mr. C. He will receive and pass data.

import "./App.css";
import { createContext, useContext } from "react";

const LanguageContext = createContext();
Enter fullscreen mode Exit fullscreen mode

Step 2. Pass the data to Context

Using the Provider, pass the data you want to pass to the LanguageContext you just created.
This is like passing the data to your mutual friend B.

Note

・The range that can receive the data passed to LanguageContext is only the range enclosed by LanguageContext.
・The value to be written in LanguageContext.Provider must be ‘value’.

import "./App.css";
import { createContext, useContext } from "react";

const LanguageContext = createContext();

//  Added code below
function App() {
  const languages = ["JavaScript", "Python", "Java", "Golang"];
  return (
    <LanguageContext.Provider value={{ languages }}>
      <div className="background">
        Background 
        <p className="languages">
          Pass languages[JavaScript, Python, Java, Golang] to Container 3
        </p>
        <Container1 />
      </div>
    </LanguageContext.Provider>
Enter fullscreen mode Exit fullscreen mode

Step 3. Writing parts that are not related to data transfer

The code is cleaner than the code without React Context because there is no need to receive or pass prop.

import "./App.css";
import { createContext, useContext } from "react";

const LanguageContext = createContext();

function App() {
  const languages = ["JavaScript", "Python", "Java", "Golang"];
  return (
    <LanguageContext.Provider value={{ languages }}>
      <div className="background">
        Background 
        <p className="languages">
          Pass languages[JavaScript, Python, Java, Golang] to Container 3
        </p>
        <Container1 />
      </div>
    </LanguageContext.Provider>
  );
}

// Added code below
function Container1() {
  return (
    <div className="container1">
      Container 1
      <Container2 />
    </div>
  );
}

function Container2() {
  return (
    <div className="container2">
      Container 2
      <Container3 />
    </div>
  );
Enter fullscreen mode Exit fullscreen mode

Step 4. Receive data from Context

In the part of the component that you want to receive data (Container3 in this case), use useContext() to receive the data.
For arguments in useContext(), put context = LanguageContext. And it will be useContext(LanguageContext). Then, you can receive the data passed by the Provider.

import "./App.css";
import { createContext, useContext } from "react";

const LanguageContext = createContext();

function App() {
  const languages = ["JavaScript", "Python", "Java", "Golang"];
  return (
    <LanguageContext.Provider value={{ languages }}>
      <div className="background">
        Background 
        <p className="languages">
          Pass languages[JavaScript, Python, Java, Golang] to Container 3
        </p>
        <Container1 />
      </div>
    </LanguageContext.Provider>
  );
}

function Container1() {
  return (
    <div className="container1">
      Container 1
      <Container2 />
    </div>
  );
}

function Container2() {
  return (
    <div className="container2">
      Container 2
      <Container3 />
    </div>
  );

// Added code below
function Container3() {
  const ctx = useContext(LanguageContext);
  console.log(ctx.languages);
  return <p>{ctx.languages}</p>;
}
Enter fullscreen mode Exit fullscreen mode

Then, you are able to receive the data as you expect.
["JavaScript", "Python", "Java", "Golang"];

Step5. Use the received data

Then, you just use the data as always. And this is the completed code.

import "./App.css";
import { createContext, useContext } from "react";

const LanguageContext = createContext();

function App() {
  const languages = ["JavaScript", "Python", "Java", "Golang"];
  return (
    <LanguageContext.Provider value={{ languages }}>
      <div className="background">
        Background 
        <p className="languages">
          Pass languages[JavaScript, Python, Java, Golang] to Container 3
        </p>
        <Container1 />
      </div>
    </LanguageContext.Provider>
  );
}

function Container1() {
  return (
    <div className="container1">
      Container 1
      <Container2 />
    </div>
  );
}

function Container2() {
  return (
    <div className="container2">
      Container 2
      <Container3 />
    </div>
  );

// Added code below
function Container3() {
  const ctx = useContext(LanguageContext);
  console.log(ctx.languages);
  return (
    <div className="container3">
      Container 3
      <ul className="languages-area">
        {ctx.languages.map((language, i) => (
          <li key={i}>{language}</li>
        ))}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Note

Normally, you would create a folder called “store” and create the LanguageContext.js in it. And create a context and export it, but in this case, for ease of viewing, I’ve included everything in the same file.

Summary

These are the steps to use React Context:
Step1. import React Context, and create React Context
Step2. Pass the data to Context
Step3. Write components whatever you want
Step4. Receive data from Context
Step5. Use the received data

The code I wrote in this post including css is in Github

Tiugo image

Modular, Fast, and Built for Developers

CKEditor 5 gives you full control over your editing experience. A modular architecture means you get high performance, fewer re-renders and a setup that scales with your needs.

Start now

Top comments (0)

Neon image

Next.js applications: Set up a Neon project in seconds

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Get started →

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay