When and How to Use Context in React

In React, context is a powerful feature that allows you to share data between components without passing props explicitly at each level of the component tree. With the introduction of hooks in React, managing context has become even more convenient.

What is React context?

React context is a feature in React that allows you to share data between components without explicitly passing it through the component tree using props. It provides a way to pass data down the component hierarchy, making it accessible to any component that needs it, regardless of their level of nesting.

Context is useful when you have data that is required by many components in your application, eliminating the need to manually pass props through multiple intermediary components. It simplifies the process of sharing state or other global data, enabling components to consume this shared data directly.

BenefitsDrawbacks
It can make your code more modular and reusable.It can be difficult to debug if you’re not familiar with how it works.
It can help you avoid prop drilling, which can make your code more difficult to read and maintain.It can be difficult to test if you’re not using a testing library that supports context.
It can improve the performance of your application by reducing the number of props that need to be passed down the component tree.It can be overused, which can lead to problems with performance and scalability.

What is the useContext hook?

The useContext hook in React provides a simpler and more concise way to access the value of a context within a functional component. To use useContext, you start by creating a context object using React.createContext(). This function accepts an initial value for the context and an optional callback function that gets triggered when the context value changes.

After creating the context object, you can directly utilize the useContext hook in a functional component to access the context value. Pass the context object as an argument to useContext, and it will return the current value of the context.

Example of how to use the useContext hook:

import { useContext, createContext } from 'react';

const MyContext = createContext(null);

const MyComponent = () => {
    const value = useContext(MyContext);
    // Do something with the value of the context
};

What problems does React context solve?

Prop drilling: Context eliminates the necessity of passing props through multiple intermediary components, which can become burdensome and result in prop drilling. Instead, it offers a simpler and cleaner approach to data sharing between components, promoting a more straightforward and streamlined development process.

Overuse of props: When you notice a pattern of passing the same prop to numerous components throughout your codebase, it may indicate an overuse of props. This practice can complicate the readability and maintainability of your code, and it may even result in performance issues.

Cross-Component Communication: When components are not directly connected through a parent-child relationship, it can be challenging to share data between them. Context allows components to communicate and share data without the need for direct prop passing or complex event handling.

Performance: When props are passed down through multiple levels of components, it can adversely affect performance. This occurs because React is required to re-render all the components affected by the prop change, even if the change only impacts a single component. Consequently, this can lead to a performance degradation.

How do I use React context?

Here I provide simple login, logout example using context. This is very basic example to understand React Context.

Creating the Context: First, you need to create a context using the createContext function provided by React.

Create file called AuthProvider.js

import React, { createContext, useContext, useState } from "react";

// Creating AuthContext using createContext function
const AuthContext = createContext(null);

// Inside AuthProvider component, managing state and functions
export const AuthProvider = ({ children }) => {
   const [isUser, setIsUser] = useState(false)

   const login = () => {
        setIsUser(true)        
   }

   const logout = () => {
        setIsUser(false)        
   }

   return <AuthContext.Provider value={{ isUser, login, logout }}>{children}</AuthContext.Provider>;
}

export const useAuth = () => {
    return useContext(AuthContext);
};

In the above code, the AuthProvider component maintains the isUser state variable and the login and logout functions. These are passed as values within the Provider component, making them accessible to the child components.

The custom hook useAuth is created to encapsulate the usage of the AuthContext and can be used in functional components to consume the isUser state and the login and logout functions.

Note that the context and hook need to be imported into the relevant components for proper usage.

Create new file called Home.js. Here we will use our AuthContext with the help of hooks useAuth.

import React from "react";
import {useAuth} from './AuthProvider'

const Home = () => {
    const { login, logout, isUser } = useAuth();

    return (
       <div>
          {
            isUser 
            ?
             <div>
               User is login <button onClick={logout}>Logout</button>
             </div>
            :
             <div>
               User is not login <button onClick={login}>Login</button>
             </div>
           }
       </div>
    )
}

export default Home;

In above code we simply update isUser value using login, logout button, without passing props.

In App.js file

import React from "react";
import {BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./Home";

import { AuthProvider } from "./AuthProvider";

function App() {
   <AuthProvider>
      <Router basename='/' >
         <Routes>
            <Route path='/' element={<Home />} />
         </Routes>
      </Router>
   </AuthProvider>
}

To provide the context value to the child components, you need to wrap them with the Provider component. You can see in above code we import AuthProvider and wrap Router in AuthProvider.

Now you will get isUser value from any component that wrap in AuthProvider also you can change isUser value using login, logout functions without passing props.

By following these steps, you can effectively use React context to share data between components.