Context API & useReducer

0/3 in this phase0/36 across the roadmap

📖 Concept

Context API is React's built-in solution for global state. It provides a way to pass data through the component tree without having to pass props down manually at every level.

The Workflow:

  1. createContext: Define the context.
  2. Provider: Wrap your app (or a part of it) and provide the value.
  3. useContext: Consume the value in any child component.

Combining with useReducer: For complex global state, the most powerful built-in pattern is to store the dispatch function from useReducer in a Context. This creates a "Redux-lite" system without any external dependencies.

Important Warning: Context is not a state management tool; it's a dependency injection tool. It has one major drawback: whenever the context value changes, all components consuming that context will re-render, even if they only use a small piece of the value.

💻 Code Example

codeTap to expand ⛶
1import React, { createContext, useContext, useReducer } from 'react';
2
3// 1. Define Contexts (Split state and dispatch for better performance)
4const AuthStateContext = createContext();
5const AuthDispatchContext = createContext();
6
7// 2. Reducer
8const authReducer = (state, action) => {
9 switch (action.type) {
10 case 'LOGIN':
11 return { ...state, isAuthenticated: true, user: action.payload };
12 case 'LOGOUT':
13 return { ...state, isAuthenticated: false, user: null };
14 default:
15 return state;
16 }
17};
18
19// 3. Provider Component
20export function AuthProvider({ children }) {
21 const [state, dispatch] = useReducer(authReducer, {
22 isAuthenticated: false,
23 user: null
24 });
25
26 return (
27 <AuthStateContext.Provider value={state}>
28 <AuthDispatchContext.Provider value={dispatch}>
29 {children}
30 </AuthDispatchContext.Provider>
31 </AuthStateContext.Provider>
32 );
33}
34
35// 4. Custom Hooks for easier consumption
36export const useAuthState = () => useContext(AuthStateContext);
37export const useAuthDispatch = () => useContext(AuthDispatchContext);
38
39// 5. Usage in a component
40function UserProfile() {
41 const { user, isAuthenticated } = useAuthState();
42 const dispatch = useAuthDispatch();
43
44 if (!isAuthenticated) return <button onClick={() => dispatch({ type: 'LOGIN', payload: { name: 'Dan' } })}>Login</button>;
45
46 return (
47 <div>
48 <p>Welcome, {user.name}!</p>
49 <button onClick={() => dispatch({ type: 'LOGOUT' })}>Logout</button>
50 </div>
51 );
52}

🏋️ Practice Exercise

  1. Implement a 'ThemeContext' that toggles between 'light' and 'dark' modes for the whole app.
  2. Build a 'LanguageSelector' using Context to provide translations (i18n).
  3. Create a 'Global State' object with nested properties. Use a component to update only one property and observe which other components re-render.
  4. Refactor a 'Prop Drilling' scenario (3+ levels) using Context.

⚠️ Common Mistakes

  • Putting everything into a single 'GlobalContext' (leading to unnecessary re-renders).

  • Using Context for highly frequent updates (like a mouse position or a fast timer).

  • Forgetting to wrap the app in the Provider, causing useContext to return undefined.

  • Not splitting state and dispatch contexts, which forces components that only need to dispatch to re-render when state changes.

💼 Interview Questions

🎤 Mock Interview

Practice a live interview for Context API & useReducer