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:
- Create the Context (like a container to hold your data).
- Provide the Context (wrap the components that need access to the data).
- 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:
- State Management:
useState
holds theuser
data and thesetUser
function to update it. - Provider Component:
UserContext.Provider
makes theuser
andsetUser
accessible to all child components. 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 theUserContext
.
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:
useContext(UserContext)
fetches theuser
value fromUserContext
.- The
user
object is displayed in theHeader
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:
setUser
is used to update the user state.- Clicking the button changes the
name
while keeping theemail
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
- No Props Drilling: Data is shared directly without passing props through every level.
- Reusable Contexts: Create multiple contexts for different purposes (e.g., user, theme).
- Simpler State Management: Easier than using Redux for small to medium-sized apps.
Where to Use Context API?
- Global State: Authentication, themes, user settings.
- Data Sharing: Passing shared data like language preferences or cart items.
- 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
- Create Context: Use
React.createContext()
to create a context object. - Provide Context: Wrap a provider component around your app or parts of it to supply the context value.
- Consume Context: Use
useContext()
in function components orContext.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
- Multiple Contexts: Both
UserContext
andThemeContext
are used in the app. - External Data Source: Context values are imported from external files (
userData.js
andthemeData.js
). - Dynamic Updates: The theme can be toggled dynamically using
toggleTheme
.
Result
- User information is displayed in the
UserInfo
component. - A
ThemeSwitcher
button allows toggling between light and dark themes. - 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? 😊