React useContext Hook
Sharing data across components without prop drilling
🌐 What is useContext?
useContext is a React Hook that lets you read and subscribe to context from your component. It provides a way to share data globally across components without passing props through every level of the tree.
import { useContext } from 'react';
function Component() {
const value = useContext(MyContext);
return <div>{value}</div>;
}
useContext Key Concepts
Global State
Share data across components
const value = useContext(Context);
No Prop Drilling
Skip intermediate components
// Direct access to context
useContext(ThemeContext)
Auto Updates
Re-render when context changes
<Context.Provider value={state}>
Multiple Contexts
Use different contexts together
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
🔹 Creating and Using Context
To use context, you first create it with createContext, then provide a value with Provider, and finally consume it with useContext. This three-step process enables global state management.
import { createContext, useContext } from 'react';
// Step 1: Create context
const ThemeContext = createContext('light');
// Step 2: Provide context value
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
// Step 3: Consume context
function Toolbar() {
const theme = useContext(ThemeContext);
return <div>Current theme: {theme}</div>;
}
Output:
🔹 Theme Switcher Example
A common use case for context is theme management. This example shows how to create a theme context that allows any component to access and change the current theme.
import { createContext, useContext, useState } from 'react';
// Create theme context
const ThemeContext = createContext();
// Theme provider component
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(theme === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// Component using theme
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
onClick={toggleTheme}
style={{
background: theme === 'light' ? '#fff' : '#333',
color: theme === 'light' ? '#000' : '#fff'
}}
>
Toggle Theme (Current: {theme})
</button>
);
}
// App with provider
function App() {
return (
<ThemeProvider>
<ThemedButton />
</ThemeProvider>
);
}
Output:
🔹 User Authentication Context
Context is perfect for managing user authentication state. This pattern allows any component to check if a user is logged in and access user information without prop drilling.
import { createContext, useContext, useState } from 'react';
// Create auth context
const AuthContext = createContext();
// Auth provider
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = (username) => {
setUser({ name: username });
};
const logout = () => {
setUser(null);
};
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
// Component using auth
function UserProfile() {
const { user, login, logout } = useContext(AuthContext);
if (!user) {
return (
<button onClick={() => login('John')}>
Login
</button>
);
}
return (
<div>
<p>Welcome, {user.name}!</p>
<button onClick={logout}>Logout</button>
</div>
);
}
Output (logged out):
🔹 Multiple Contexts
You can use multiple contexts in the same component to access different pieces of global state. Each context operates independently and can be updated separately.
import { createContext, useContext } from 'react';
const ThemeContext = createContext();
const UserContext = createContext();
const LanguageContext = createContext();
function Dashboard() {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
const language = useContext(LanguageContext);
return (
<div>
<p>Theme: {theme}</p>
<p>User: {user.name}</p>
<p>Language: {language}</p>
</div>
);
}
function App() {
return (
<ThemeContext.Provider value="dark">
<UserContext.Provider value={{ name: 'John' }}>
<LanguageContext.Provider value="en">
<Dashboard />
</LanguageContext.Provider>
</UserContext.Provider>
</ThemeContext.Provider>
);
}
Output:
Theme: dark
User: John
Language: en
🔹 Custom Context Hook
Creating a custom hook for your context makes it easier to use and provides better error handling. This pattern ensures the context is used within the correct provider.
import { createContext, useContext, useState } from 'react';
const CartContext = createContext();
// Custom hook for cart context
function useCart() {
const context = useContext(CartContext);
if (!context) {
throw new Error('useCart must be used within CartProvider');
}
return context;
}
// Cart provider
function CartProvider({ children }) {
const [items, setItems] = useState([]);
const addItem = (item) => {
setItems([...items, item]);
};
return (
<CartContext.Provider value={{ items, addItem }}>
{children}
</CartContext.Provider>
);
}
// Component using custom hook
function ShoppingCart() {
const { items, addItem } = useCart();
return (
<div>
<p>Items in cart: {items.length}</p>
<button onClick={() => addItem('Product')}>
Add Item
</button>
</div>
);
}
Output:
Items in cart: 0
🔹 useContext Best Practices
✅ Do's:
- Create custom hooks: Wrap useContext in custom hooks
- Split contexts: Use separate contexts for different concerns
- Provide defaults: Set default values in createContext
- Memoize values: Use useMemo for complex context values
❌ Don'ts:
- Don't overuse: Not everything needs to be in context
- Don't put everything in one context: Split by concern
- Don't forget Provider: Always wrap with Provider
- Don't use for frequent updates: Can cause performance issues