Skip to main content

Command Palette

Search for a command to run...

Understand React Context API Easily

Introduction

Updated
•9 min read•View as Markdown
Understand React Context API Easily
S

I am a developer from Nairobi Kenya

React Context API has become an integral part of modern React development, offering an elegant solution to state management that simplifies the process of passing data through your application.

In this guide, we'll explore the React Context API in-depth, providing a solid understanding of its key concepts and demonstrating how to leverage its power for efficient state management.

Prerequisites

  • Basic Understanding of React

  • Familiarity with functional components and React hooks.

  • Node.js and npm (Node Package Manager)

As React evolves and component trees get larger, it becomes harder for developers to maintain state in a single location at the root of the component tree for a complex application.

Passing state down and up the component tree through dozens of components is tedious and bug-hidden. This is described in the diagram below

In the above diagram, the components A, B, C, and D are nested. Suppose component D needs data from the root component A. Notice how data is passed from component A down to component D. This data must pass through components B and C even though they might not need the data. This is known as prop drilling.

The React Context API allows you to share data between components without passing it through the entire component tree.

Analogy

In React, Context is like jet-setting for your data. You place data in React Context by creating a Context Provider.

A Context Provider is a React component that you wrap around your entire component tree or specific sections of your component tree.

The Context Provider is the departing airport where your data boards the plane. It is also the airline hub. All flights depart from the airport to different destinations. Each destination is a Context Consumer.

The Context Consumer is the React component that retrieves the data from the Context. This is the destination airport where your data lands.

The following diagram will help you understand the entire concept of React Context.

Now, from the above diagram, Components B and D retrieve data from a central repository in this case let us refer to it as the Context. The data does not have to be passed through the entire component tree. See, components A and C do not need the data therefore they do not retrieve it.

The Data is available for all components that may need it. The components retrieving the data are the Consumer components.

We will follow the below flowchart for easier understanding.

Start

To get started, create your React app and open it in your favorite IDE. I will use Visual Studio Code.

Use the following line of command to create your react app faster.

NPM

npm create vite@latest

Yarn

yarn create vite

Create a Context

Before we create the context, In the src folder, create a folder and name it components

In the components folder create two files

  • Login.jsx

  • Profile.jsx

The Login.jsx file is a functional component that will contain the following➖

  • Two states that hold the username and the password.

  • A function that handles the submitted username and password.

  • Input fields that accept username and password.

  • A button to submit the username and password.

This is the code for the Login.jsx file

import React, { useState } from "react";

const Login = () => {

  // State to manage username and password input fields
  const [username, setUsernamme] = useState("");
  const [password, setPassword] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();
    //logic for logging in.
  };

  return (
    <div>
      <h2>Log In</h2>

      <div>
          {/* Input fields for username and password */}
        <input
          type="text"
          value={username}
          placeholder="username"
          onChange={(e) => setUsernamme(e.target.value)}
        />
        <input
          type="password"
          value={password}
          placeholder="password"
          onChange={(e) => setPassword(e.target.value)}
        />
        <button type="button" onClick={handleSubmit}>
          submit
        </button>
      </div>
    </div>
  );
};

export default Login;

The Profile.jsx file is also a functional component that doesn't do much, only displays the username.

This is the code for the Profile.jsx file

import React from "react";

const Profile = () => {

  return (
    <div>
      <h3>Profile</h3>
      <p>Username : {" "}</p> 
    </div>
  );
};

export default Profile;

Import the two files Login.jsx and Profile.jsx in the App.jsx to be rendered.

Your App.jsx file should look as follows

import Login from './components/Login'
import Profile from './components/Profile'

function App() {

  return (
    <>
      <Login /> 
      <Profile /> 
    </>
  )
}

export default App

Now let’s proceed to create our context.

  • Create a folder, name it context

  • In the context folder, create a file named UserContext.jsx

A context might be for any purpose, therefore you can name it according to it use e.g ThemeContext for handling themes or ProductContext for handling product data. For this guide, we create a context for handling user data that’s why I named it UserContext. Keep it in mind you can name it however you like.

Let me first provide the code for the UserContext.jsx file then explain it for you to understand.

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

// Create a new context named UserContext
export const UserContext = createContext();

// Define a component called UserContextProvider
const UserContextProvider = ({ children }) => {

  // Use the useState hook to create state for the user
  const [user, setUser] = useState(null);

  // Render the UserContext.Provider, wrapping the children components
  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
}

// Export the UserContextProvider component as the default export
export default UserContextProvider;

Now, let's break it down step by step:

  1. Import Statements:

    • The code starts by importing the necessary modules from React. It imports createContext and useState from 'react'. These are essential for creating a context and managing state, respectively.
  2. Create UserContext:

    • The code creates a new context named UserContext using the createContext function. This context will be used to share and manage the state related to the user throughout the application.
  3. UserContextProvider Component:

    • The UserContextProvider component is defined. This component will act as the provider for the UserContext. It takes a children prop, which represents the components wrapped by this provider.
  4. useState for User:

    • Inside the UserContextProvider component, the useState hook is used to create a piece of state called user with an initial value of null. This state will hold information about the user.
  5. UserContext.Provider:

    • The UserContext.Provider component is used to wrap the child components. It takes a value prop, which is an object containing the user state and the setUser function. This makes the user state and its setter available to any component that consumes the UserContext.
  6. Rendering Children:

    • The children prop is rendered inside the UserContext.Provider. This allows the components within the UserContextProvider to access the UserContext and consume the shared state.
  7. Export UserContextProvider:

    • The UserContextProvider component is exported as the default export. This allows other parts of your application to import and use this context provider.

Provide the context to all components

Now let us make the context available to all components. We achieve this with the below code.

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import UserContextProvider from "./context/UserContext.jsx";

ReactDOM.createRoot(document.getElementById("root")).render(
  // Wrap the entire application with React.StrictMode
  <React.StrictMode>
    {/* Use the UserContextProvider to provide the UserContext to all components */}
    <UserContextProvider>
      {/* Render the main App component */}
      <App />
    </UserContextProvider>
  </React.StrictMode>
);

Let's break it down step by step:

  1. UserContextProvider:

    • Inside React.StrictMode, the UserContextProvider component is used. This is the provider you created for the UserContext. By wrapping your entire application with this provider, you ensure that the UserContext is available to all components within the application.
  2. App Component:

    • The main App component is rendered inside the UserContextProvider. This means that all child components of App and their descendants will have access to the UserContext.

Consume the Context in child components

The components that consume the UserContext are the Login.jsx and the Profile.jsx files.

The following is the code and explanations of how the two components consume the context.

**Login.jsx:

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

const Login = () => {

  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");

  // Access setUser function from UserContext using useContext
  const { setUser } = useContext(UserContext);

  // Handle form submission
  const handleSubmit = (e) => {
    e.preventDefault();
    // Update the user context with the entered username and password
    setUser({ username, password });
  };

  return (
    <div>
      <h2>Log In</h2>

      <div>
        {/* Input fields for username and password */}
        <input
          type="text"
          value={username}
          placeholder="username"
          onChange={(e) => setUsername(e.target.value)}
        />
        <input
          type="password"
          value={password}
          placeholder="password"
          onChange={(e) => setPassword(e.target.value)}
        />
        {/* Button to submit the form */}
        <button type="button" onClick={handleSubmit}>
          Submit
        </button>
      </div>
    </div>
  );
};

export default Login;

Explanation:

  • The Login component uses the useContext hook to access the UserContext.

  • It maintains local state for username and password input fields using the useState hook.

  • The handleSubmit function is called when the form is submitted, updating the user context with the entered username and password.

**Profile.jsx:

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

const Profile = () => {
  // Access the user object from UserContext using useContext
  const { user } = useContext(UserContext);

  // Check if the user is logged in
  if (!user) {
    return <h1>Not Logged In</h1>;
  }

  return (
    <div>
      <h3>Profile</h3>
      {/* Display the username from the user context */}
      <p>Username: {user.username}</p>
    </div>
  );
};

export default Profile;

Explanation:

  • The Profile component uses the useContext hook to access the UserContext.

  • It checks if the user object exists in the context. If not, it displays a message indicating that the user is not logged in.

  • If the user is logged in, it renders the user's profile with the username retrieved from the user context.

In summary, the Login component updates the UserContext with the entered credentials, and the Profile component retrieves and displays the user information from the UserContext. This demonstrates how components can seamlessly consume and interact with shared state through the React Context API.

Conclusion

In wrapping up our exploration of the React Context API, we've witnessed its transformative impact on state management within React applications.

The dynamic interplay between the Login and Profile components showcased the simplicity and elegance of data transmission through the shared UserContext.

As you continue your React journey, integrating the Context API into your toolkit empowers you to build more modular and maintainable applications.

With its streamlined approach to state management, React Context API eliminates the complexities of prop drilling, allowing for a more cohesive and scalable architecture.

Your understanding of the core concepts of creating a context, providing it through a provider, and consuming it in components, opens doors to efficient solutions for various state-related challenges. Whether it's managing themes, user authentication, or other shared states, React Context API offers a robust and flexible solution.

Thank you for investing your time in exploring this guide. Your commitment to mastering React's state management capabilities positions you for success in your development endeavors.

For more insights and updates, consider subscribing to my newsletter. Stay tuned for future tutorials, and happy coding!