Prop Drilling and working with Context API, useContext Hook in React

Working with props is great! They provide simple mechanism to pass state and other information with components in the hierarchy.

Similar to what we have seen earlier with lifting state in React, what if we want to pass information from a top level component to children probably at the bottom of that tree?

One way is to keep passing the required information through props from subsequent children to the ones which actually needs it. But it may not be an optimal solution, because components who do not need access to that information and have to play role of middleware unnecessarily. This situation is called as "Prop Drilling" in React. 


Prop Drilling even though its not a official term has a solution in the form of Context API or with Redux.


As you can see above, we can provide a context from the root element of the component tree and the children(s) which needs access to use can use the context created earlier.

When we use Context API to create a context, it returns a provider and consumer.

We can set a default context value as well. Provider works as a distributor. Since the introduction of useContext hook, we no longer need consumer.

To use the context API, find a root component which has the information. Create a context at the root component and setup the provider at the root of the component tree so it can wrap all the children which needs information 

It is recommended to use Context API where props needs to be passed down at-least 3-4 levels down, Otherwise, props should be used.

Creating Context

The context object should be a global object so that any children can access it.


  //create global context
  const BookLibraryContext = createContext();

Setting up Provider at Root Component

To set values required by children, use a property exposed by Provider called as valueWe can use any type of value to provide the needed abstraction. This property can hold functions, objects, arrays etc. 

   
//value property can hold any type of value which will be exposed to children
      <BookLibraryContext.Provider value={{ purchasedBooks }}>
        <section className="booklist">
          <h1 className="display-4">Welcome to the React Library!</h1>
          <Purchase purchasedBooks={purchasedBooks} />
        </section>
      </BookLibraryContext.Provider>
   

useContext

useContext is a hook and similar to every hook, it should be imported as a named import. 

Create an object of useContext and pass the context object to access the context, in our case, its BookLibraryContext.

Make sure the context object is correctly destructured as per the value set by the parent or root. Child components can pick and choose what they want from the context.

 
//create a context object for the BookLibraryContext
  const { purchasedBooks } = useContext(BookLibraryContext);

JS File

Notice how the component tree looks like, similar to our problem statement, where we wanted to pass props from BookLibrary to Inventory without actually passing props through children. 


  //create global context
  const BookLibraryContext = createContext();

  //Parent
  export const BookLibrary = () => {
    //moving state Up!
    const books = [
      {
            id: 3,
            title: "The Alchemist",
            author: "Paulo Cohlo",
            coverImage: "./images/the-alchemist.jpg",
        },
        {
            id: 4,
            title: "Grandma's Bag of Stories",
            author: "Sudha Murthy",
            coverImage: "./images/grandams-bag-of-stories.jpg",
        },
        {
            id: 5,
            title: "The Power of Your Subconscious Mind",
            author: "Joseph Murphy",
            coverImage: "./images/the-power-of-your-subconscious-mind.jpg",
            special: true,
        },
    ];
    const [purchasedBooks, setPurchasedBooks] = useState(books);

    return (
      <>
        <BookLibraryContext.Provider value={{ purchasedBooks }}>
          <section className="booklist">
            <h1 className="display-4">Welcome to the React Library!</h1>
            <Purchase purchasedBooks={purchasedBooks} />
          </section>
        </BookLibraryContext.Provider>
      </>
    );
  };

  //Child 2 (Sibling 2)
  const Purchase = () => {
    return (
      <>
        <h1>Verifying Purchase Activies... </h1>
        <Address />
      </>
    );
  };

  const Address = () => {
    return (
      <>
        <h2>Before sending your books to your address</h2>
        <Inventory />
      </>
    );
  };

  const Inventory = () => {
    //create a context object for the BookLibraryContext
    const { purchasedBooks } = useContext(BookLibraryContext);

    return (
      <>
        <div className="row">
          <h3>Checking Inventory for your purchased books below:</h3>
          <div className="col-md-4"></div>
          <div
            className="col-md-4 add-column-margin"
            style={{ marginTop: 30, maxHeight: 250 }}
          >
            <div id="myCarousel" className="carousel slide" data-ride="carousel">
              <ol className="carousel-indicators">
                {purchasedBooks.map((book, index) => {
                  return (
                    <li
                      key={index}
                      data-target="#myCarousel"
                      data-slide-to={index}
                      className={index === 0 ? "active" : ""}
                    ></li>
                  );
                })}
              </ol>
              <div className="carousel-inner">
                {purchasedBooks.map((book, index) => {
                  return (
                    <div
                      key={index}
                      className={
                        index === 0 ? "carousel-item active" : "carousel-item"
                      }
                    >
                      <img
                        className="d-block w-100"
                        src={book.coverImage}
                        alt="First slide"
                      ></img>
                      <div class="carousel-caption d-none d-md-block">
                        <h5
                          style={{
                            color: "white",
                            backgroundColor: "rgba(0, 0, 0, 0.6)",
                          }}
                        >
                          {book.title}
                        </h5>
                      </div>
                    </div>
                  );
                })}
              </div>
              <a
                className="carousel-control-prev"
                href="#myCarousel"
                role="button"
                data-slide="prev"
              >
                <span
                  className="carousel-control-prev-icon"
                  aria-hidden="true"
                ></span>
                <span className="sr-only">Previous</span>
              </a>
              <a
                className="carousel-control-next"
                href="#myCarousel"
                role="button"
                data-slide="next"
              >
                <span
                  className="carousel-control-next-icon"
                  aria-hidden="true"
                ></span>
                <span className="sr-only">Next</span>
              </a>
            </div>
          </div>
        </div>
      </>
    );
  };

  ReactDOM.render(<BookLibrary />, document.getElementById("root"));


Output











Comments

Popular Posts