Let’s start from zero knowledge and build a solid understanding of the React Context API, step by step. I will explain everything in simple terms.


What is the Context API?

The Context API in React is a way to share data (state, functions) across multiple components without passing props manually at every level. This avoids props drilling (passing props through many layers of components).


Why Use Context API?

Imagine you have an app with this structure:

App
 ├── Header
 │     └── UserName (needs `user` data)
 ├── Sidebar
 │     └── ThemeToggle (needs `theme` data)
 └── MainContent
       └── PostList (needs `posts` data)

Here’s the problem:
If user, theme, or posts data is in App, you need to pass props from App to every child component, even if some don’t directly use that data.

Example of props drilling (inefficient):

<App user={user} theme={theme} posts={posts}>
  <Header user={user} />
  <Sidebar theme={theme} />
  <MainContent posts={posts} />
</App>

With the Context API, you can share these values directly with any component without passing them as props.


How Does the Context API Work?

There are 3 key steps in using the Context API:

  1. Create the Context (like a container to hold your data).
  2. Provide the Context (wrap the components that need access to the data).
  3. Consume the Context (use the data wherever needed).

Step-by-Step Example: Sharing User Data

Let’s build a small app where we share user data (name and email) with multiple components.


1. Create Context

First, create a context for the user data.

UserContext.js

import { createContext } from "react";
 
// Create the UserContext
export const UserContext = createContext();

Here, we are:

  • Using createContext from React.
  • Exporting the context so it can be used elsewhere.

2. Create a Provider

Next, create a provider component that will hold the data and provide it to other components.

UserProvider.js

import React, { useState } from "react";
import { UserContext } from "./UserContext";
 
const UserProvider = ({ children }) => {
  // State to hold user data
  const [user, setUser] = useState({
    name: "Md. Mojnu Miah",
    email: "mojnu@example.com",
  });
 
  return (
    // Use the context's provider to pass the user data
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
};
 
export default UserProvider;

Here’s what’s happening:

  1. State Management: useState holds the user data and the setUser function to update it.
  2. Provider Component: UserContext.Provider makes the user and setUser accessible to all child components.
  3. children Prop: Allows any component inside this provider to access the data.

3. Wrap the App with the Provider

Wrap your app in the UserProvider so all child components can access the user data.

index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import UserProvider from "./contexts/UserProvider";
 
ReactDOM.render(
  <UserProvider>
    <App />
  </UserProvider>,
  document.getElementById("root")
);

This ensures:

  • Any component inside <App /> can use the UserContext.

4. Consume the Context

Now, let’s access the user data in a component using useContext.

Header.js

import React, { useContext } from "react";
import { UserContext } from "../contexts/UserContext";
 
const Header = () => {
  const { user } = useContext(UserContext); // Access the user data
 
  return (
    <header style={{ padding: "10px", background: "#f0f0f0" }}>
      <h1>Welcome, {user.name}!</h1>
      <p>Email: {user.email}</p>
    </header>
  );
};
 
export default Header;

Here’s what’s happening:

  1. useContext(UserContext) fetches the user value from UserContext.
  2. The user object is displayed in the Header component.

5. Updating the Context

Let’s add a button to update the user’s name dynamically.

Profile.js

import React, { useContext } from "react";
import { UserContext } from "../contexts/UserContext";
 
const Profile = () => {
  const { user, setUser } = useContext(UserContext);
 
  const updateName = () => {
    setUser({ ...user, name: "Updated Mojnu Miah" });
  };
 
  return (
    <div style={{ padding: "10px" }}>
      <h2>Profile</h2>
      <p>Name: {user.name}</p>
      <p>Email: {user.email}</p>
      <button onClick={updateName} style={{ marginTop: "10px" }}>
        Update Name
      </button>
    </div>
  );
};
 
export default Profile;

Here’s what’s happening:

  1. setUser is used to update the user state.
  2. Clicking the button changes the name while keeping the email unchanged (...user).

6. Full App Code

App.js

import React from "react";
import Header from "./components/Header";
import Profile from "./components/Profile";
 
const App = () => {
  return (
    <div>
      <Header />
      <Profile />
    </div>
  );
};
 
export default App;

Benefits of Context API

  1. No Props Drilling: Data is shared directly without passing props through every level.
  2. Reusable Contexts: Create multiple contexts for different purposes (e.g., user, theme).
  3. Simpler State Management: Easier than using Redux for small to medium-sized apps.

Where to Use Context API?

  1. Global State: Authentication, themes, user settings.
  2. Data Sharing: Passing shared data like language preferences or cart items.
  3. Avoiding Props Drilling: When components deep in the tree need access to data.

When NOT to Use Context API?

  • Frequently Changing State: Context re-renders every consumer when the value changes. For performance-heavy apps, consider tools like Redux or Zustand.
  • Simple State: For small apps, local state (useState) is enough.

React Context API - Step-by-Step Guide with an Advanced Example

The React Context API is a way to pass data through the component tree without manually passing props at every level. It’s useful for managing global state, like themes, user authentication, or configurations.


Key Steps in Using Context API

  1. Create Context: Use React.createContext() to create a context object.
  2. Provide Context: Wrap a provider component around your app or parts of it to supply the context value.
  3. Consume Context: Use useContext() in function components or Context.Consumer in class components to access the value.

Building an Advanced App with Multiple Context Providers

Setup:

We’ll create a UserContext for user info and a ThemeContext for managing the app's theme. The context values will come from external files.


File Structure:

/src
  /contexts
    UserContext.js
    ThemeContext.js
  /data
    userData.js
    themeData.js
  App.js
  index.js

Step 1: Create External Data Files

/data/userData.js

export const userData = {
  name: "Md. Mojnu Miah",
  role: "Frontend Developer",
  country: "Bangladesh",
};

/data/themeData.js

export const themeData = {
  light: {
    background: "#ffffff",
    color: "#000000",
  },
  dark: {
    background: "#000000",
    color: "#ffffff",
  },
};

Step 2: Create Context Providers

/contexts/UserContext.js

import React, { createContext, useState } from "react";
import { userData } from "../data/userData";
 
export const UserContext = createContext();
 
const UserProvider = ({ children }) => {
  const [user, setUser] = useState(userData);
 
  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
};
 
export default UserProvider;

/contexts/ThemeContext.js

import React, { createContext, useState } from "react";
import { themeData } from "../data/themeData";
 
export const ThemeContext = createContext();
 
const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState(themeData.light);
 
  const toggleTheme = () => {
    setTheme((prevTheme) =>
      prevTheme === themeData.light ? themeData.dark : themeData.light
    );
  };
 
  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};
 
export default ThemeProvider;

Step 3: Combine Providers in App.js

App.js

import React from "react";
import UserProvider from "./contexts/UserContext";
import ThemeProvider from "./contexts/ThemeContext";
import MainComponent from "./components/MainComponent";
 
const App = () => {
  return (
    <UserProvider>
      <ThemeProvider>
        <MainComponent />
      </ThemeProvider>
    </UserProvider>
  );
};
 
export default App;

Step 4: Create Components to Use Context

/components/MainComponent.js

import React from "react";
import UserInfo from "./UserInfo";
import ThemeSwitcher from "./ThemeSwitcher";
 
const MainComponent = () => {
  return (
    <div>
      <h1>React Context API Example</h1>
      <UserInfo />
      <ThemeSwitcher />
    </div>
  );
};
 
export default MainComponent;

/components/UserInfo.js

import React, { useContext } from "react";
import { UserContext } from "../contexts/UserContext";
 
const UserInfo = () => {
  const { user } = useContext(UserContext);
 
  return (
    <div>
      <h2>User Information:</h2>
      <p>Name: {user.name}</p>
      <p>Role: {user.role}</p>
      <p>Country: {user.country}</p>
    </div>
  );
};
 
export default UserInfo;

/components/ThemeSwitcher.js

import React, { useContext } from "react";
import { ThemeContext } from "../contexts/ThemeContext";
 
const ThemeSwitcher = () => {
  const { theme, toggleTheme } = useContext(ThemeContext);
 
  return (
    <div
      style={{
        background: theme.background,
        color: theme.color,
        padding: "20px",
        marginTop: "20px",
        borderRadius: "5px",
      }}
    >
      <h2>Theme Switcher:</h2>
      <button onClick={toggleTheme} style={{ padding: "10px", cursor: "pointer" }}>
        Toggle Theme
      </button>
    </div>
  );
};
 
export default ThemeSwitcher;

Key Features of This Example

  1. Multiple Contexts: Both UserContext and ThemeContext are used in the app.
  2. External Data Source: Context values are imported from external files (userData.js and themeData.js).
  3. Dynamic Updates: The theme can be toggled dynamically using toggleTheme.

Result

  1. User information is displayed in the UserInfo component.
  2. A ThemeSwitcher button allows toggling between light and dark themes.
  3. The context values are globally accessible and managed without prop-drilling.

Would you like to explore any part of this further?

Does this explanation make things clear? Would you like more examples or further simplification? 😊