Creating Context
Creating Context
Context provides a way to pass data through the component tree without props drilling.
Basic Context
import { createContext, useContext, useState } from "react";
// Create context
const ThemeContext = createContext(null);
// Provider component
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>
);
}
// Custom hook for using context
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
}
// Usage
function App() {
return (
<ThemeProvider>
<Header />
<Main />
</ThemeProvider>
);
}
function Header() {
const { theme, toggleTheme } = useTheme();
return (
<header>
<button onClick={toggleTheme}>Toggle Theme</button>
<span>Current: {theme}</span>
</header>
);
}
Default Values
// With default value
const ThemeContext = createContext("light");
// Without provider, uses default value
function Component() {
const theme = useContext(ThemeContext); // "light"
}
Multiple Contexts
const AuthContext = createContext(null);
const ThemeContext = createContext(null);
function App() {
return (
<AuthProvider>
<ThemeProvider>
<Layout />
</ThemeProvider>
</AuthProvider>
);
}
Provider Pattern
Provider Pattern
Complete Provider Example
import { createContext, useContext, useState, useMemo } from "react";
const AuthContext = createContext(null);
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const login = async (email, password) => {
try {
setLoading(true);
setError(null);
const response = await fetch("/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password })
});
const data = await response.json();
setUser(data.user);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
const logout = () => {
setUser(null);
};
// Memoize value to prevent unnecessary re-renders
const value = useMemo(
() => ({
user,
loading,
error,
login,
logout,
isAuthenticated: !!user
}),
[user, loading, error]
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
// Custom hook
function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within an AuthProvider");
}
return context;
}
Nested Providers
function App() {
return (
<AuthProvider>
<ThemeProvider>
<NotificationProvider>
<Layout />
</NotificationProvider>
</ThemeProvider>
</AuthProvider>
);
}
// Each component can access any provider
function Header() {
const { user } = useAuth();
const { theme } = useTheme();
const { notifications } = useNotification();
}
Provider Composition
// Combine multiple providers
function AppProviders({ children }) {
return (
<AuthProvider>
<ThemeProvider>
<NotificationProvider>
{children}
</NotificationProvider>
</ThemeProvider>
</AuthProvider>
);
}
// Usage
function App() {
return (
<AppProviders>
<Layout />
</AppProviders>
);
}
useContext Hook
useContext Hook
Basic Usage
const ThemeContext = createContext("light");
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button className={theme}>Click me</button>;
}
Accessing Context Values
function UserProfile() {
const { user, logout } = useAuth();
const { theme, toggleTheme } = useTheme();
if (!user) return <p>Please log in</p>;
return (
<div>
<h2>{user.name}</h2>
<button onClick={logout}>Logout</button>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}
Context with Performance
// Problem: All consumers re-render when any value changes
const ValueContext = createContext({ a: 0, b: 0 });
// Solution: Split into separate contexts
const AContext = createContext(0);
const BContext = createContext(0);
function Provider({ children }) {
const [a, setA] = useState(0);
const [b, setB] = useState(0);
return (
<AContext.Provider value={a}>
<BContext.Provider value={b}>
{children}
</BContext.Provider>
</AContext.Provider>
);
}
// Now components only re-render when their specific value changes
function ComponentA() {
const a = useContext(AContext); // Only re-renders when a changes
}
function ComponentB() {
const b = useContext(BContext); // Only re-renders when b changes
}
Context vs Redux
| Feature | Context | Redux |
|---|---|---|
| Complexity | Simple | Complex |
| Bundle size | Small | Larger |
| DevTools | No | Yes |
| Middleware | No | Yes |
| Performance | Good for simple | Optimized |
Use Context for:
- Theme, language, auth
- Simple global state
- Application settings
Use Redux for:
- Complex state logic
- Frequent updates
- Need for devtools/middleware
Practice Problems
Create a reusable React component implementing Context API. Include proper state management and accessibility.
Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where neededWrite unit and integration tests for Context API using React Testing Library.
Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility testsOptimize Context API for performance. Consider memoization, code splitting, and bundle size.
Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysisQuiz
1. What is React Context used for?
2. What does useContext return?
3. What happens when context value changes?
4. When should you use Context?
Flashcards
Question
What is React Context?
Click to reveal answer
Answer
A way to pass data through component tree without props drilling
Question
What does useContext return?
Click to reveal answer
Answer
The value of the nearest Context Provider
Question
When do context consumers re-render?
Click to reveal answer
Answer
When the context value changes
Question
When should you use Context vs Redux?
Click to reveal answer
Answer
Context for simple global state, Redux for complex state logic
Question
What is Context API?
Click to reveal answer
Answer
Context API is a key concept in frontend development.
Revision Notes
Key Takeaways
- 1.Context avoids props drilling
- 2.useContext consumes context values
- 3.All consumers re-render when value changes
- 4.Split contexts for performance
- 5.Use for simple global state
Interview Tips
- •Explain when to use Context vs Redux
- •Show how to create a Context Provider
- •Demonstrate avoiding props drilling
Cheat Sheet
Cheat Sheet
Create Context
const ThemeContext = createContext(null);
Provider
<ThemeContext.Provider value={{ theme, toggle }}>
{children}
</ThemeContext.Provider>
useContext
const { theme, toggle } = useContext(ThemeContext);
Custom Hook
function useTheme() {
const context = useContext(ThemeContext);
if (!context) throw new Error("...");
return context;
}