React Context API — Global State Management
DodaTech
3 min read
In this tutorial, you'll learn about React Context API. We cover key concepts, practical examples, and best practices.
What You'll Learn
Use React's Context API to share global state across components — create context, provide values, consume with useContext, optimize performance, and avoid prop drilling.
Why It Matters
Without Context, you pass data through every level of the component tree (prop drilling). Context gives any component direct access to shared data.
Real-World Use
Sharing a user's authentication status, theming (light/dark mode), locale/language preferences, or a shopping cart across the entire app.
The Prop Drilling Problem
// ❌ Without Context — prop drilling
function App() {
const [user, setUser] = useState(null);
return <Header user={user} />;
}
function Header({ user }) {
return (
<nav>
<UserMenu user={user} />
</nav>
);
}
function UserMenu({ user }) {
return <Avatar user={user} />;
}
function Avatar({ user }) {
return <img src={user.avatar} alt={user.name} />;
}
// ✅ With Context — direct access
function Avatar() {
const { user } = useContext(UserContext);
return <img src={user.avatar} alt={user.name} />;
}
Creating and Using Context
import { createContext, useContext, useState } from "react";
// Step 1: Create context
const AuthContext = createContext(null);
// Step 2: Create provider component
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
function login(email, password) {
// In reality, call an API
setUser({ email, name: "Alice" });
}
function logout() {
setUser(null);
}
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
// Step 3: Custom hook for consuming
function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within AuthProvider");
}
return context;
}
// Step 4: Use it anywhere
function LoginButton() {
const { user, login, logout } = useAuth();
if (user) {
return (
<div>
Welcome, {user.name}
<button onClick={logout}>Logout</button>
</div>
);
}
return <button onClick={() => login("a@b.com", "pass")}>Login</button>;
}
// Step 5: Wrap your app
function App() {
return (
<AuthProvider>
<Header />
<MainContent />
</AuthProvider>
);
}
Theme Context Example
const ThemeContext = createContext("light");
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
const toggleTheme = () => {
setTheme(prev => prev === "light" ? "dark" : "light");
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
onClick={toggleTheme}
style={{
background: theme === "dark" ? "#333" : "#fff",
color: theme === "dark" ? "#fff" : "#333",
padding: "10px",
border: "1px solid #ccc",
}}
>
Switch to {theme === "light" ? "Dark" : "Light"} Mode
</button>
);
}
Multiple Contexts
function App() {
const [theme, setTheme] = useState("light");
const [user, setUser] = useState(null);
const [cart, setCart] = useState([]);
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<AuthContext.Provider value={{ user, setUser }}>
<CartContext.Provider value={{ cart, setCart }}>
<MainApp />
</CartContext.Provider>
</AuthContext.Provider>
</ThemeContext.Provider>
);
}
Performance Optimization
Context re-renders ALL consumers when the value changes:
// ❌ Problem: every keypress re-renders all context consumers
function App() {
const [text, setText] = useState("");
return (
<SearchContext.Provider value={{ text, setText }}>
<ExpensiveComponent /> {/* Re-renders on every keypress */}
</SearchContext.Provider>
);
}
// ✅ Solution: split contexts or use useMemo
function App() {
const [text, setText] = useState("");
const searchValue = useMemo(
() => ({ text, setText }),
[text]
);
return (
<SearchContext.Provider value={searchValue}>
<ExpensiveComponent />
</SearchContext.Provider>
);
}
Context vs Redux
| Aspect | Context | Redux |
|---|---|---|
| Complexity | Simple | More setup |
| Boilerplate | Minimal | Actions, reducers, store |
| Performance | All consumers re-render | Selectors prevent re-renders |
| Middleware | None | Redux Thunk, Saga |
| DevTools | Basic | Excellent |
| Scale | Small-medium apps | Large apps |
| Learning curve | Low | Medium |
When to Use Context
✅ User authentication / session
✅ Theme (light/dark mode)
✅ Language / locale preferences
✅ Feature flags
✅ Small-medium apps without complex state
❌ Frequently updating data (consider Redux, Zustand)
❌ Complex state logic (consider useReducer + Context)
❌ Very large apps (consider Redux or Zustand)
← Previous
useEffect Hook — Side Effects in React
Next →
useReducer Hook — Complex State Logic in React
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro