React
Chapter 10: Context API & State Sharing — Solving prop drilling, createContext, Provider patterns, useContext hook, custom context wrappers, and performance tuning.
1. The Prop Drilling Problem & Why Context Exists
In standard React applications, data is passed top-down via props. When deeply nested components (like a UserAvatar on line 500) need access to global state (like theme or currentUser), you are forced to pass props through dozens of intermediate components that don't even use the data. This is called Prop Drilling.
💡 Real-World Analogy: Radio Broadcast Tower vs Passing Notes in Class
Think of distributing news to everyone in a stadium:
- Prop Drilling (Passing Notes): You hand a piece of paper to the person next to you, who hands it to the next person, through 50 rows of people until it reaches row 50. Fragile, tedious, and messy!
- React Context (Radio Broadcast Tower): The parent installs a radio broadcast tower (Provider). Any component anywhere in the stadium can simply tune their antenna to that frequency (useContext hook) and receive the broadcast instantly without bothering anyone else!
2. The 3 Core Steps to Using React Context
Complete Production Implementation
// 1. Create Context with default value
const ThemeContext = React.createContext("light");
// 2. Wrap App with Provider at high level
function App() {
const [theme, setTheme] = React.useState("dark");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Header />
<MainDashboard />
</ThemeContext.Provider>
);
}
// 3. Consume anywhere deep inside the tree without props drilling!
function ThemeToggleButton() {
const { theme, setTheme } = React.useContext(ThemeContext);
return (
<button
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
className={`btn-${theme}`}
>
Current Theme: {theme.toUpperCase()}
</button>
);
}
useContext(ThemeContext)
3. The Custom Provider & Custom Hook Pattern
In enterprise applications, never export raw Context objects directly. Instead, encapsulate the Provider and expose a clean, type-safe custom hook (e.g. useAuth()):
// AuthContext.jsx
const AuthContext = React.createContext(null);
export function AuthProvider({ children }) {
const [user, setUser] = React.useState({ name: "Anshuman", role: "admin" });
const login = (userData) => setUser(userData);
const logout = () => setUser(null);
return (
<AuthContext.Provider value={{ user, login, logout, isAuthenticated: !!user }}>
{children}
</AuthContext.Provider>
);
}
// Custom consumer hook with safety guard:
export function useAuth() {
const context = React.useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within an AuthProvider!");
}
return context;
}
4. Hands-On Practice Challenges
Master global state management and Context provider architectures with these interactive challenges. Click "Solve Challenge" to open the workspace!
Challenge 1: Replicate the Context API Architecture
Write a factory function createMiniContext(defaultValue) that returns a Provider(newValue) and useContext() consumer function.
- Store
currentValueinitialized todefaultValue. Provider(newValue)updates current value and notifies subscribers.useContext()returns the current value.
Initial Context Value: "light" | Updated Context Value: "dark"
createMiniContext to manage shared state across components.
Challenge 2: Context Guard Hook Validator
Write a guard validator validateContextConsumer(contextValue, hookName) that throws a descriptive error if the consumer is accessed outside its Provider.
- Check if
contextValue === undefined || contextValue === null. - Throw descriptive error message containing
hookName. - Otherwise return the valid
contextValuesafely.
Caught Expected Error: [Context Guard]: useAuth must be wrapped inside its corresponding ContextProvider!
Challenge 3: Full AuthContext Store Simulation
Implement an createAuthStore() object containing getState(), login(userProfile), and logout() to simulate an enterprise AuthProvider.
- Encapsulate
authState = { user: null, isAuthenticated: false }. login(userProfile)updates user and setsisAuthenticated: true.logout()resets state back tonullandfalse.
State: { user: { id: 101, name: "Anshuman Singh", ... }, isAuthenticated: true } | State after logout: { user: null, isAuthenticated: false }
createAuthStore with login/logout action dispatchers.