A Guide to the React useContext Hook

A Guide to the React useContext Hook

In this article, you will learn why React’s useContext hook makes it easy to manage states globally. Context defined will be available to child components without manually passing props down the tree. Kent C . Dodds called it prop drilling.

This article assumes you have an understanding of React and, React hooks in functional components.

useContext makes up part of the React context API which many third party libraries like Redux try to solve. You can read Dan Abramov's You Might Not Need Redux. useContext is a clean and easy way to share state between components that allows us to pass data down to components to grant global components access to re-render when data change in the global state instead of prop drilling.

According to React's doc: A component calling useContext will always re-render when the context value changes. If re-rendering the component is expensive, you can optimize it by using memoization.

Managing data without useContxt

Managing data with the state should be done at the parent component in the components tree that will be passed to the child component. We will use the student data below to illustrate a first look at the parent component.

import React, { useState } from "react";

const FirstComponent = () => {
  const [studentDetails, setStudentDetails] = useState({
    name: "John",
    age: 25
  });

  return (
    <div>
      <h1>This is the Parent Component</h1>
    </div>                                         
  )
}

let's add more child components based on hierarchy to access available data from the parent components.

import React, { useState } from "react";

const FirstComponent = () => {
 const [studentDetails, setStudentDetails] = useState({
    name: "John",
    age: 25
  });

  return (
    <div>
      <h1>This is the Parent Component</h1>
       <SecondComponentst studentDetails={studentDetails} />

    </div>                                         
  )
}

const SecondComponent = ({studentDetails}) => {
  return (
    <div>
      <h2>This is Second Component</h2><br/>
      <ThirdComponent studentDetails={studentDetails} />
    </div>
  )
}

const ThirdComponent = ({studentDetails}) => {
  return (
    <div>
      <h3>This is Third Component</h3>
     <ForthComponentst studentDetails={studentDetails} />

     </div>
  )
}

const ForthComponent = ({studentDetails}) => {
  return (
    <div>
      <h3>This is Forth Component</h3>
     <h4>{`Welcome back ${studentDetails}!`}</h4>
    </div>
  )
}

In the above illustration, the state declared in the parent components will be passed through props to the child components and will continue with data display as long as the prop drilling continues. This may be overlooked when it's just one component but in big projects prop drilling will not be advised.

The problem here is that even though we don't want the presiding components to share data with the parent components, they had to pass the state along with the drill so that it could reach the later component.

Defining the state data at the top level allows us to explicitly pass down the hierarchy of the child component through props.

The useContext solution

The React Context API comes to our rescue instead of passing props to data through the component tree where data may not be required.

We will be using the same data but this time we will be creating studentContext.Provider at the parent component of our data.

Firstly, let's create a top-level custom provider component createContext file.

import React, {createContext} from 'react';

const StudentContext = createContext(null);

export { StudentProvider };

Now, let's import our file into the App components which happen to be our parent component.

import React from "react";
import {StudentProvider} from "./StudentContext";
...
const FirstComponent = () => {
  const [studentDetails, setStudentDetails] = useState({
    name: "John",
    age: 25
  });

  return (
    <StudentContext.Provider value={studentDetails}>
      <h1>{`Welcome back ${studentDetails}!`}</h1>
      <ForthComponent studentDetails={studentDetails} />
    </StudentContext.Provider>
  );
}

We can now access the student Context at any level of the component without prop drilling.

const ForthComponent = () => {
const student = useContext(StudentContext);

  return (
    <div>
      <h3>This is Forth Component</h3>
     <h4>{`Welcome back ${studentDetails}!`}</h4>
    </div>
  )
}

Our data has been successfully passed to the ForthComponent but we must access the context created at the parent component in other to pass data without passing it as props.

Here is the full example using React Context:

import React from "react";
import {StudentProvider} from "./StudentContext";
...
const FirstComponent = () => {
  const [studentDetails, setStudentDetails] = useState({
    name: "John",
    age: 25
  });

  return (
    <StudentContext.Provider value={studentDetails}>
      <h1>{`Welcome back ${studentDetails}!`}</h1>
      <ForthComponent studentDetails={studentDetails} />
    </StudentContext.Provider>
  );
}

const SecondComponent = () => {
  return (
    <div>
      <h2>This is Second Component</h2>
      <SubChildComponent />
    </div>
  )
}

const ThirdComponent = () => {
  return (
    <div>
      <h3>This is Third Component</h3>
     <ForthComponent />
     </div>
  )
}

const ForthComponent = () => {
const student = useContext(StudentContext);

  return (
    <div>
      <h3>This is Forth Component</h3>
     <h4>{`Welcome back ${studentDetails}!`}</h4>
    </div>
  )
}

Conclusion

useContext hooks has ushered new frontier to state management and that does not negate big projects may require us to use state management library like Redux Toolkit which has reduced the complexity of using Redux in our projects.