Context API Patterns
Before reaching for Redux or Zustand, you should master React’s built-in Context API. It’s perfect for low-frequency updates like themes, user authentication, or language preferences.
However, it has a dark side: Performance. If used incorrectly, Context can cause your entire app to re-render on every keystroke.
1. The Problem: Prop Drilling
Prop drilling occurs when you pass data through components that don’t need it, just to get it to a deep child component.
Interactive: Prop Drilling Visualizer
Visualize how data travels. Click a node to see the path.
2. Pattern: The Split Context
A common mistake is putting both state and dispatch (or setter functions) into the same context object.
// ❌ BAD: Re-renders everything when *either* changes
const MyContext = createContext({ state, dispatch });
If a component only needs dispatch (e.g., a button), it will still re-render when state changes!
The Solution: Split Components
Create two separate contexts.
import { createContext, useContext, useState, ReactNode } from 'react';
// Define context types
interface User {
name: string;
email: string;
}
type StateContextType = User | null;
type DispatchContextType = (user: User | null) => void;
const StateContext = createContext<StateContextType>(null);
const DispatchContext = createContext<DispatchContextType>(() => {});
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null);
return (
<StateContext.Provider value={user}>
<DispatchContext.Provider value={setUser}>
{children}
</DispatchContext.Provider>
</StateContext.Provider>
);
}
// Custom Hooks for consumption
export const useUser = () => useContext(StateContext);
export const useSetUser = () => useContext(DispatchContext);
Now, components that only need setUser (like a Logout button) won’t re-render when user changes (like updating the profile name).
3. When to use Context vs Redux/Zustand?
| Criteria | Context API | Zustand / Redux |
|---|---|---|
| Frequency | Low (Themes, Auth, Locale) | High (Dashboard data, Real-time feeds) |
| Complexity | Simple values or small objects | Complex nested objects, arrays |
| DevTools | Basic (React DevTools) | Advanced (Time Travel, Diffing) |
| Performance | Requires manual optimization (Split/Memo) | Optimized out of the box (Selectors) |
[!IMPORTANT] Context is NOT a state management library. It is a dependency injection mechanism. It doesn’t manage state; it transports it. You still use
useStateoruseReducerto manage the actual data.