Posted on 5/16/2025 9:12:48 AM by Admin

Simplifying State Management With React's Context API

As the complexity of your React applications increases, you come across scenarios when two or more nested components share the same data. Although this can easily be achieved by passing the props down the component tree, it can lead to Prop Drilling. Prop drilling refers to passing the props through intermediate components that don't need them. This phenomenon makes your code difficult to maintain and refactor. Luckily, we have a powerful built-in solution to this problem in React called the Context API.

In this article, I'll introduce you to the Context API and its simple use cases for managing the states that are accessed by multiple components.

Understanding Prop Drilling

As mentioned earlier, the prop drilling is a scenario where the props are passed down to the nested components using the intermediate components that actually do not need them. Suppose we have a deeply nested component named ChildComponent that needs to access the data inside the top-level component, i.e., AppComponent. The standard way of passing this data down would be using props in a way that the AppComponent passes the prop to the GrandParentComponent, which then passes it to the ParentComponent, which in turn passes it down to the ChildComponent.

AppComponent GrandParentComponent ParentComponent ChildComponent

Although this approach works well in simple cases where few props have to be passed down, as the complexity of your components and the number of props to be passed increase, it becomes difficult to manage the code. The intermediate components become aware of the data they don't even need. This makes it harder to reuse and modify the code. Changing the data structure at the top level can affect the entire component tree, including those that had no concern with the data. This makes the prop drilling a pain for the developers.

Introduction to Context API

The Context API provides an efficient way of dealing with the shared states, i.e., by creating a global shared space for storing data that any component can access at any level within the component tree. This excludes the need to pass down the props throughout the hierarchy. The Context API depends upon the three core concepts:

  1. Creating Context: The first step in using the Context API is defining a context using React.createContext() method. This function returns a Provider and a Consumer component. An optional default value can also be passed to the createContext() method to be used by a consuming component that is not wrapped in a matching provider.
    Syntax:
            
            const NewContext = React.createContext(null); // Creating a context
            
            
  2. The Provider: The context.Provider component makes the component data available to all its descendants components. A value prop is passed to this component to store data to be shared, which can be accessed by any component wrapped inside the Provider.
    Example:
            
            function App() {
              const dataToShare = { message: 'I am shared from Context!' };
              return (
                <NewContext.Provider value={dataToShare}>
                  {/* Components that can access sharedData */}
                  <ChildComponent />
                </NewContext.Provider>
              );
            }
            
            
  3. The Consumer: A consumer accesses the data provided by the Provider with the help of the useContext() hook. The Context object is passed to the useContext(), and it returns the value of the Context.
    Example:
            
            import React, { useContext } from 'react';
            const NewContext = React.createContext(null);
    
            function ChildComponent() {
              const contextData = useContext(NewContext);
              return <p>{contextData.message}</p>; // Accessing the shared data
            }
            
            

Simple Context Use Case: Setting Up Theme

One of the most common and simple use cases to understand how the Context API works is setting the theme of the application. Let's create a new project named Theme.js and add the following code to it:


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

const ContextTheme = createContext({ theme: 'light', themeSetter: () => {} });


function ThemeProvider() {
  return (
    <div>
      <Header />
      <Content />
      <Footer />
    </div>
  );
}

function Header() {
  const { theme, themeSetter } = useContext(ContextTheme);
  return (
    <div style={{ backgroundColor: theme === 'light' ? '#eee' : '#333', color: theme === 'light' ? '#333' : '#eee', padding: '10px' }}>
      <h1>My New Theme</h1>
      <button onClick={() => themeSetter(theme === 'light' ? 'dark' : 'light')}>Toggle Theme</button>
    </div>
  );
}

function Content() {
  const { theme } = useContext(ContextTheme);
  return (
    <div style={{ backgroundColor: theme === 'light' ? 'white' : 'black', color: theme === 'light' ? 'black' : 'white', padding: '20px' }}>
      <p>This is the main content of the page</p>
    </div>
  );
}

function Footer() {
  const { theme } = useContext(ContextTheme);
  return (
    <div style={{ backgroundColor: theme === 'light' ? '#ccc' : '#555', color: theme === 'light' ? '#333' : '#eee', padding: '5px', textAlign: 'center' }}>
      <p>Footer - Theme: {theme}</p>
    </div>
  );
}

export default ThemeProvider;

In the above code, we defined the Context and the state variable theme with a default value of light. Inside ThemeProvider, we call different components to display on the screen. Then, we defined the theme and content for each section in the header, content, and footer. Inside each component, we use the useContext() hook to access the current state of the Context. Inside the Header, we specify a button that toggles the theme when clicked by updating the Context State. This way, each component can access a single state without needing to pass down the theme variable through multiple components.

In App.js, we first import the useState hook to set the initial state to light.


import logo from './logo.svg';
import './App.css';
import React, { useState } from 'react';
import Theme from './Theme';

function App() {
  const [theme, themeSetter] = useState('light');

  return (
    <Theme/>
  );
}


export default App;

Our app looks as follows when rendered:

Toggling Theme with Context
Toggling Theme with Context

When to Use Context?

Using Context API in every situation is not an ideal approach. It should be used in cases where the data has to be accessed globally within the specific parts of your components. Some examples include:

  1. Specifying themes and typography
  2. User authentication
  3. Setting language preferences

However, in scenarios where the data is changed frequently and needed only by a small number of related components, passing down the props is a better option. Overusing the Context API to manage local state can make your components harder to test in isolation. Moreover, it can lead to unnecessary re-renders if many components access the same context.


Sharpen Your Skills with These Next Guides