Introduction to React Context API
In React, managing state and passing data from a parent component to deeply nested child components can sometimes become cumbersome. This problem, commonly known as prop drilling, occurs when data must be passed through multiple layers of components that do not necessarily need it.
The Context API, introduced in React 16.3, provides a built-in solution for global state management. It allows developers to share data across multiple components without passing props manually at every level. Context is particularly useful for theme management, user authentication, language settings, and other global application data.
In this post, we will cover:
- How to create a context
- Providing context values
- Consuming context in child components
- A practical example: building a theme switcher (dark/light mode)
Creating a Context
The first step in using the Context API is creating a context object using React.createContext(). This object serves as a central store for the data you want to share globally.
Basic Syntax
import React from 'react';
// Create a context with an optional default value
const MyContext = React.createContext(defaultValue);
export default MyContext;
defaultValueis optional and used when a component consumes the context without a provider.- The
MyContextobject includes two main components:- Provider: Wraps components and provides context values.
- Consumer: Accesses context values inside child components.
Example: Creating a User Context
import React from 'react';
// Creating a context to store user data
const UserContext = React.createContext({
name: "Guest",
role: "visitor"
});
export default UserContext;
Providing Context Values
Once the context is created, we use the Provider component to supply values to all child components. The Provider wraps the component tree and makes the context available to all nested components.
Syntax
<Context.Provider value={/* any value */}>
{/* child components */}
</Context.Provider>
- The
valueprop can contain any data type: object, array, number, function, etc. - All children wrapped inside the provider can access the context value.
Example: Providing User Data
import React, { useState } from 'react';
import UserContext from './UserContext';
import UserProfile from './UserProfile';
function App() {
const [user, setUser] = useState({ name: "Alice", role: "admin" });
return (
<UserContext.Provider value={user}>
<UserProfile />
</UserContext.Provider>
);
}
export default App;
Explanation:
UserContext.ProviderwrapsUserProfile.- Any component inside the provider can now consume the
userobject.
Consuming Context in Child Components
There are several ways to access context values in child components:
1. Using useContext Hook (Recommended for Functional Components)
import React, { useContext } from 'react';
import UserContext from './UserContext';
function UserProfile() {
const user = useContext(UserContext);
return (
<div>
<h2>User Profile</h2>
<p>Name: {user.name}</p>
<p>Role: {user.role}</p>
</div>
);
}
export default UserProfile;
Explanation:
useContext(UserContext)retrieves the current context value.- No need to pass props through intermediate components.
- Automatically subscribes to context changes, re-rendering the component when the value updates.
2. Using Context Consumer Component
For class components or alternative usage:
import React from 'react';
import UserContext from './UserContext';
function UserProfile() {
return (
<UserContext.Consumer>
{user => (
<div>
<h2>User Profile</h2>
<p>Name: {user.name}</p>
<p>Role: {user.role}</p>
</div>
)}
</UserContext.Consumer>
);
}
export default UserProfile;
Explanation:
UserContext.Consumeruses a render prop to access context values.- Less common in modern React since
useContextis more concise.
Example: Theme Switcher (Dark/Light Mode)
Let’s build a practical example using the Context API: a theme switcher. The theme context will allow any component to access and toggle between dark and light themes.
Step 1: Create Theme Context
import React from 'react';
const ThemeContext = React.createContext({
theme: "light",
toggleTheme: () => {}
});
export default ThemeContext;
Step 2: Provide Theme Context in App Component
import React, { useState } from 'react';
import ThemeContext from './ThemeContext';
import ThemedButton from './ThemedButton';
function App() {
const [theme, setTheme] = useState("light");
const toggleTheme = () => {
setTheme(prevTheme => (prevTheme === "light" ? "dark" : "light"));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
<div style={{
backgroundColor: theme === "light" ? "#fff" : "#333",
color: theme === "light" ? "#000" : "#fff",
padding: "20px"
}}>
<h1>React Theme Switcher</h1>
<ThemedButton />
</div>
</ThemeContext.Provider>
);
}
export default App;
Explanation:
themestate stores the current theme.toggleThemefunction toggles between light and dark themes.ThemeContext.Providerpasses boththemeandtoggleThemeto child components.
Step 3: Consume Theme in Child Component
import React, { useContext } from 'react';
import ThemeContext from './ThemeContext';
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
onClick={toggleTheme}
style={{
padding: "10px 20px",
backgroundColor: theme === "light" ? "#000" : "#fff",
color: theme === "light" ? "#fff" : "#000",
border: "none",
cursor: "pointer"
}}
>
Toggle Theme
</button>
);
}
export default ThemedButton;
Explanation:
useContext(ThemeContext)provides access to the current theme and toggle function.- The button style changes dynamically based on the theme.
- Clicking the button toggles the theme for all components consuming the context.
Benefits of Using Context API
- Avoids Prop Drilling
- Components can access context directly without passing props through multiple layers.
- Centralized State Management
- Ideal for managing global data like themes, user authentication, or language settings.
- Improves Maintainability
- Makes it easy to modify and extend global state without changing intermediate components.
- Seamless Integration with Hooks
- Works well with
useState,useReducer, and custom hooks.
- Works well with
Best Practices for Context API
- Use Context for Global Data Only: Don’t use it for every small state; keep it for shared or persistent data.
- Separate Contexts for Different Data: Avoid combining unrelated data in a single context to reduce unnecessary re-renders.
- Memoize Provider Value: Use
useMemoto prevent re-rendering all consumers unnecessarily.
Example: Optimizing Provider
import React, { useState, useMemo } from 'react';
import ThemeContext from './ThemeContext';
function App() {
const [theme, setTheme] = useState("light");
const toggleTheme = () => {
setTheme(prev => (prev === "light" ? "dark" : "light"));
};
const value = useMemo(() => ({ theme, toggleTheme }), [theme]);
return (
<ThemeContext.Provider value={value}>
{/* child components */}
</ThemeContext.Provider>
);
}
Explanation:
useMemoensures the provider value object is recreated only whenthemechanges, reducing unnecessary re-renders of consumers.
Leave a Reply