Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
useContext is a Hook that lets you subscribe to React context without introducing nesting. It's the solution to the "prop drilling" problem where you need to pass data through many levels of components.
First, create a context object using createContext(). Think of it as creating a special container for sharing data across components.
Pro Tip
The default value is only used when a component tries to consume context without being wrapped in a Provider. Use meaningful defaults!
Wrap components with Context.Provider and pass the value. This makes the data available to all child components!
Key Point
The Provider's value prop can be anything - object, string, number, function, or even another component!
Use useContext Hook to access the context value. This is how child components can read the shared data!
Important
useContext returns the context value for the context you pass. Make sure you're using the same context object you created!
Context updates automatically when the Provider's value changes. All consuming components will re-render with the new value!
Performance Note
When the Provider's value changes, ALL components using that context will re-render. Keep values stable when possible!
Create custom hooks for cleaner code reuse and better error handling. This encapsulates context logic!
Best Practice
Custom hooks provide type safety, error handling, and a cleaner API for your context!
Global theme management with context
// Theme Context
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = React.useState('light');
const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
function useTheme() {
const context = React.useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// Components using the theme
function Header() {
const { theme, toggleTheme } = useTheme();
return (
<header className={`header header-${theme}`}>
<h1>My App</h1>
<button onClick={toggleTheme}>
Switch to {theme === 'light' ? 'Dark' : 'Light'} Mode
</button>
</header>
);
}
function Content() {
const { theme } = useTheme();
return (
<main className={`content content-${theme}`}>
<h2>Welcome to {theme === 'light' ? 'Light' : 'Dark'} Mode!</h2>
<p>This content adapts to the current theme.</p>
</main>
);
}
function App() {
return (
<ThemeProvider>
<div className="app">
<Header />
<Content />
</div>
</ThemeProvider>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);Loading preview...
Global user state with context
// Auth Context
const AuthContext = createContext();
function AuthProvider({ children }) {
const [user, setUser] = React.useState(null);
const [isLoading, setIsLoading] = React.useState(false);
const login = async (email, password) => {
setIsLoading(true);
try {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000));
const userData = { id: 1, name: 'John Doe', email };
setUser(userData);
} catch (error) {
console.error('Login failed:', error);
} finally {
setIsLoading(false);
}
};
const logout = () => {
setUser(null);
};
const value = {
user,
isLoading,
login,
logout
};
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
function useAuth() {
const context = React.useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}
// Components using auth
function LoginForm() {
const { login, isLoading } = useAuth();
const [email, setEmail] = React.useState('');
const [password, setPassword] = React.useState('');
const handleSubmit = (e) => {
e.preventDefault();
login(email, password);
};
return (
<form onSubmit={handleSubmit} className="login-form">
<h2>Login</h2>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<button type="submit" disabled={isLoading}>
{isLoading ? 'Logging in...' : 'Login'}
</button>
</form>
);
}
function UserProfile() {
const { user, logout } = useAuth();
return (
<div className="user-profile">
<h2>Welcome, {user.name}!</h2>
<p>Email: {user.email}</p>
<button onClick={logout}>Logout</button>
</div>
);
}
function AppContent() {
const { user } = useAuth();
return (
<div className="app">
<header className="header">
<h1>My App</h1>
</header>
<main className="main">
{user ? <UserProfile /> : <LoginForm />}
</main>
</div>
);
}
function App() {
return (
<AuthProvider>
<AppContent />
</AuthProvider>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);Loading preview...
Always wrap components that use context with the appropriate Provider. Create a wrapper component if needed.
Create context objects outside components, at module level. This ensures all components use the same context instance.
Use useMemo to stabilize context values when they contain objects or functions to prevent unnecessary re-renders.
Use context for truly global state. For component-specific state, use props or component composition.
Context eliminates the need to pass props through multiple component levels.
Perfect for user authentication, themes, and settings needed app-wide.
Create custom hooks for cleaner, type-safe context access with error handling.
Use useMemo to stabilize context values and prevent unnecessary re-renders.