Exploring React Hooks: useContext

Exploring React Hooks: useContext

Exploring the Power of useContext Hook in React

Welcome back to our Exploring React Hooks series. In our previous article, we explored how to use useEffect effectively. In this installment, we'll be focusing on the useContext hook, which enables us to easily access context values in our components. So let's get started!

Understanding Context:

Before we dive into the useContext hook, let's quickly recap what React context is.

Context allows us to share data between components that are not directly connected in the component hierarchy.

Context provides a way to pass data through the component tree without having to pass props manually at every level.

It can be very useful in scenarios where multiple components need access to the same data, such as theme preferences, localization settings, or user authentication.

What is the useContext Hook?

The useContext hook is a built-in hook provided by React that allows components to consume values from a Context.

With the useContext hook, you can access the current value of a Context directly within a functional component, eliminating the need to use a Context.Consumer component or prop drilling.

Using the useContext Hook

To utilize the useContext hook:

  1. Import and create a Context using the createContext() function.

  2. This Context can then be provided to other components using a Context.Provider component.

  3. Within the consuming components, we can import and call useContext(Context) to access the current value provided by the Context.Provider.

The Props Way

Let's say we have a scenario where there are multiple nested components and both the top and bottom components need access to the state. We can pass the state as props through each nested component. This process is commonly referred to as prop drilling.

We will be creating five components, where each component will render a heading element with a different character from the Game of Thrones series, forming a nested hierarchy of components.

Our main purpose is to access the user value inside ComponentE that will be provided by ComponentA.

ComponentA:

import React, { useState } from "react";
import ComponentB from "./ComponentB";

function ComponentA() {
  const [user, setUser] = useState("Jon Snow");

  return (
    <>
      <h2>{`${user} of House Stark!`}</h2>
      <ComponentB user={user} />
    </>
  );
}

export default ComponentA;

ComponentB:

function ComponentB({ user }) {
  return (
    <>
      <h3>Daenerys Stormborn of House Targaryen!</h3>
      <ComponentC user={user} />
    </>
  )};

ComponentC:

function ComponentC({ user }) {
  return (
    <>
      <h3>Cersei Lannister of House Lannister!</h3>
      <ComponentD user={user} />
    </>
  )};

ComponentD:

function ComponentD({ user }) {
  return (
    <>
      <h3>Robert Baratheon of House Baratheon!</h3>
      <ComponentE user={user} />
    </>
  )};

ComponentE:

import React, { useContext } from "react";
import { UserContext } from "./ComponentA";

function ComponentE({ user }) {
  return (
    <>
      <h2>{`Hello ${user}, King in the North!!`}</h2>
    </>
  );
}

export default ComponentE;

Output:

Import the ComponentA into your App Component and run the server. This should be the expected result:

In the above example, we had to use prop drilling to pass the user value from the top-level component (ComponentA) down to the nested components (ComponentB, ComponentC, ComponentD), that did not need the data themselves, only to pass it down to a deeper component(ComponentE) that required it.

The Context Way

With useContext, we can avoid this unnecessary passing of props through intermediate components. We can directly access the required data from the context without explicitly passing it through component props.

We'll be using the previous example. Let's check it out:

ComponentA

This component will serve as the top-level component and the provider of the UserContext.

To create context, we must import createContext and initialize it. We will then create and export the UserContext using the createContext() function.

We'll provide the UserContext with the value of user using the UserContext.Provider component. This will allow any nested components that consume this context to access the value.

import React, { createContext, useState } from "react";
import ComponentB from "./ComponentB";

export const UserContext = createContext();

function ComponentA() {
  const [user, setUser] = useState("Jon Snow");

  return (
    <>
      <UserContext.Provider value={user}>
        <h2>{`I am ${user} of House Stark!`}</h2>
        <ComponentB/>
      </UserContext.Provider>
    </>
  );
}

export default ComponentA;

Now, all components in this tree will have access to the UserContext.

ComponentE

In order to use the Context in a child component, we need to access it using the useContext Hook.

First, we include the useContext in the import statement. Then we can access the UserContext in this component.

import React, { useContext } from "react";
import { UserContext } from "./ComponentA";


function ComponentE() {
  const user = useContext(UserContext);

  return (
    <>
      <h2>{`Hello ${user}, King in the North!!`}</h2>
    </>
  );
}

export default ComponentE;

The UserContext is created in ComponentA and is passed down through the nested components (ComponentB, ComponentC, ComponentD, and ComponentE). The user value is provided by ComponentA and accessed by ComponentE using the useContext hook.

Output:

Import ComponentA inside App.jsx and run the server:

As evident, the resulting output remains unchanged in both cases.

However, by utilizing the useContext hook, we have effectively replaced the need for passing props down the component tree. This approach simplifies our code, enhances reusability, and makes it easier to manage shared data across the application.

Context Considerations

Here are some considerations to keep in mind when using Context:

  • Don't use Context to avoid drilling props down just one or two layers:

    Context is great for managing state which is needed by large portions of an application. However, If you only need to pass data down a few layers, prop drilling can be a faster and simpler solution.

  • Avoid using Context to save state that should be kept locally:

    If the state is specific to a particular component or doesn't need to be accessed by other components, it's best to use local state rather than Context.

  • Wrap the Provider component around the lowest common parent:

    You should always wrap the Provider around the lowest possible common parent in the tree where the shared state is needed.

To Summarize

The useContext hook can be used anywhere inside a functional component, enabling easy access to the context value without any prop drilling. Once we have the context value, we can utilize it within our component for rendering, state management, or any other purpose we require.
The useContext hook simplifies our code and allows us to create applications that are easier to scale and maintain.


References


Thank's for taking the time to read this blog. Hope you found it informative and enjoyable! Connect with me on Twitter.

Stay tuned for our next article: useRef Hook.

Catch you guys on the next one. Cheers .. ✌️

Did you find this article valuable?

Support Raj Sarkar by becoming a sponsor. Any amount is appreciated!