In complex React applications, a piece of state (like the current user theme, language preference, or authenticated status) often needs to be accessed by components located deep within the component tree, regardless of their parentage.
The Context API is React’s built-in solution for creating and sharing global data across the entire component tree without having to manually pass props down through every level (a problem known as prop drilling).
1. The Problem: Prop Drilling
Prop drilling occurs when you pass a piece of data through multiple intermediate components that don’t actually need the data themselves, just to get it to a deeply nested child.
Example: Passing theme prop: App -> Layout -> Sidebar -> ThemeToggle
2. The Context API Workflow
Context involves three main steps to establish a global channel for data:
- Creation: Use
createContext()to establish the Context object. - Provision: Use the
.Providercomponent to wrap the components that need access to the data and supply the global state. - Consumption: Use the
useContexthook in any child component to read the shared data.
3. Execution Example: Creating a Theme Context
We’ll create a simple example to toggle the application’s theme (light/dark) using Context.
A. ThemeContext.js (Creation)
Create a new file ThemeContext.js to define the Context object:
import { createContext, useContext, useState } from 'react';
// 1. Create the Context object
export const ThemeContext = createContext();
// 2. Create a custom Provider component (Best Practice)
// This component manages the state and provides it to children.
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light'); // The global state
const toggleTheme = () => {
setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
};
// The value prop passes the state and the setter function down
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
// 3. Custom Hook for consumption (Simplifies usage)
export const useTheme = () => {
return useContext(ThemeContext);
};
B. ThemeToggler.js (Consumption and Action)
This component will access the toggleTheme function directly from Context.
Create ThemeToggler.js:
import React from 'react';
import { useTheme } from './ThemeContext'; // Import the custom consumer hook
function ThemeToggler() {
// Use the custom hook to access the context values
const { theme, toggleTheme } = useTheme();
return (
<button onClick={toggleTheme}>
Switch to {theme === 'light' ? 'Dark' : 'Light'} Mode
</button>
);
}
export default ThemeToggler;
C. App.js (Provision and Display)
Open App.js. We wrap the application in the Provider and use Context to control the main style.
import React from 'react';
import { ThemeProvider, useTheme } from './ThemeContext'; // Import the Provider and Hook
import ThemeToggler from './ThemeToggler';
// Component that consumes the theme for display/styling
function MainContent() {
const { theme } = useTheme(); // Access the current theme state
const style = {
padding: '20px',
backgroundColor: theme === 'light' ? '#fff' : '#333',
color: theme === 'light' ? '#333' : '#fff',
minHeight: '200px'
};
return (
<div style={style}>
<h2>Current Theme: {theme.toUpperCase()}</h2>
<p>This content is dynamically styled by the global Context state.</p>
</div>
);
}
function App() {
return (
// 1. Wrap the entire application (or the relevant subtree) in the Provider.
<ThemeProvider>
<div className="ContextDemo">
<MainContent />
<ThemeToggler /> {/* Sibling component controlling the state */}
</div>
</ThemeProvider>
);
}
export default App;
- Local Execution: Run
npm start. - Observation: Clicking the button in
ThemeTogglercalls thetoggleThemefunction provided byThemeProvider. This updates the global state, causing all consuming components (likeMainContent) to automatically re-render with the new theme style.
