The useContext Hook
Overview
Once a Context Provider is broadcasting data, how do deeply nested components actually read it? The `useContext` hook allows any functional component to 'listen' to a Context. When the Provider's value changes, React automatically re-renders all components that are using `useContext` for that specific Context.
Syntax
Notice how `ThemedButton` doesn't take any props. It grabs the `theme` data straight out of thin air (from the nearest `ThemeContext.Provider` above it in the tree).
import { useContext } from 'react';
// Import the Context object we created earlier
import { ThemeContext } from './App';
function ThemedButton() {
// Pass the Context object to useContext to extract the current broadcasted value
const theme = useContext(ThemeContext);
return (
<button className={`btn-${theme}`}>
I am styled globally!
</button>
);
}This pattern is highly recommended. It hides the implementation details and provides a clear, helpful error message if another developer forgets to wrap their component in the Provider.
// Instead of forcing every component to import useContext AND the Context object...
// We write a custom hook in the same file as the Provider:
export function useTheme() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
}
// Usage in the component:
// import { useTheme } from './ThemeProvider';
// const theme = useTheme();Common Pitfalls
- Trying to use `useContext(ThemeContext)` in a component that is *not* a child of `ThemeContext.Provider`. It won't crash automatically; it will just silently return the default value provided to `createContext()`, causing confusing bugs.
Interview Tips
- Be aware of the performance implications. If the Provider's value changes, *every* component calling `useContext` will re-render. This is why Context is great for rarely changing data (Auth, Theme, Locale) but terrible for fast-changing data (like mouse coordinates or keystrokes).
Real-World Example
Using the AuthContext to conditionally render a Login or Logout button deep in a Sidebar component.
function Sidebar() {
// Extract both the state and the updater function
const { user, logout } = useAuth();
return (
<div className="sidebar">
{user ? (
<>
<p>Logged in as {user.email}</p>
<button onClick={logout}>Sign Out</button>
</>
) : (
<Link to="/login">Sign In</Link>
)}
</div>
);
}