βοΈ React Hook: useContext
β
π Quick Summary β
useContext
provides a way to share values (state, functions, objects) across the component tree without prop drilling.- Works with the Context API (
React.createContext
). - Syntax:tsx
const value = useContext(MyContext);
π§ Mental Model β
- Imagine React context as a broadcast station π‘.
- The
Provider
is the broadcaster that sends values. - Any child with
useContext
is a radio tuned in to receive those values. - No need to pass props down manually through multiple layers.
π Key Concepts β
Creating Context
const MyContext = React.createContext(defaultValue);
Provider
<MyContext.Provider value={something}>
- Wraps components and provides them access to the value.
Consumer
useContext(MyContext)
β retrieves the current value.- Renders with the value provided by the nearest Provider above in the tree.
Reactivity
- If the Providerβs value changes, all consuming components re-render.
Default Value
- Used when no Provider is found higher in the tree.
π» Code Examples β
Example 1: Basic Context β
import { createContext, useContext } from "react";
const ThemeContext = createContext("light");
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button className={theme}>I am {theme} themed</button>;
}
export default function App() {
return (
<ThemeContext.Provider value="dark">
<ThemedButton />
</ThemeContext.Provider>
);
}
How it works (stepβbyβstep):
- Create context with default
"light"
. - Wrap
<ThemedButton>
with<ThemeContext.Provider value="dark">
. - Inside
ThemedButton
,useContext(ThemeContext)
returns"dark"
. - Button renders with
"dark"
theme.
Example 2: Avoiding Prop Drilling β
import { createContext, useContext } from "react";
const UserContext = createContext(null);
function Profile() {
const user = useContext(UserContext);
return <h2>User: {user.name}</h2>;
}
function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<Profile />
</div>
);
}
export default function App() {
const currentUser = { name: "Ayush", role: "Admin" };
return (
<UserContext.Provider value={currentUser}>
<Dashboard />
</UserContext.Provider>
);
}
How it works (stepβbyβstep):
UserContext
created.- Provider supplies
currentUser
object. Profile
consumesuser
usinguseContext
.- No need to pass user props through
App β Dashboard β Profile
.
Example 3: Nested Providers (Multiple Contexts) β
import { createContext, useContext } from "react";
const ThemeContext = createContext("light");
const AuthContext = createContext(false);
function StatusBar() {
const theme = useContext(ThemeContext);
const isAuthenticated = useContext(AuthContext);
return (
<div className={theme}>
{isAuthenticated ? "Logged in" : "Guest"} user
</div>
);
}
export default function App() {
return (
<ThemeContext.Provider value="dark">
<AuthContext.Provider value={true}>
<StatusBar />
</AuthContext.Provider>
</ThemeContext.Provider>
);
}
How it works (stepβbyβstep):
- Two contexts created (
ThemeContext
,AuthContext
). - Providers nested, each supplies its own value.
StatusBar
consumes both using separateuseContext
calls.- Renders
"Logged in user"
with"dark"
theme.
Example 4: Updating Context Value β
import { createContext, useContext, useState } from "react";
const CounterContext = createContext(null);
function CounterDisplay() {
const { count, increment } = useContext(CounterContext);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+1</button>
</div>
);
}
export default function App() {
const [count, setCount] = useState(0);
const increment = () => setCount(c => c + 1);
return (
<CounterContext.Provider value={{ count, increment }}>
<CounterDisplay />
</CounterContext.Provider>
);
}
How it works (stepβbyβstep):
- Provider supplies
{ count, increment }
. CounterDisplay
consumes both.- Clicking
+1
updates state in parent. - Provider sends updated value down.
- Consumer re-renders with new count.
β οΈ Common Pitfalls & Gotchas β
- β Overusing context for all state β can cause unnecessary re-renders.
- β Forgetting to wrap with Provider β consumers receive default value.
- β Updating context with large objects β all consumers re-render even if they donβt use the updated part.
β Best Practices β
- Use context for global state (auth, theme, language).
- Keep context values minimal (store only whatβs needed).
- Consider splitting contexts if values update at different frequencies.
- Combine with memoization (
useMemo
) to avoid unnecessary re-renders.
β Interview Q&A β
Q1. What problem does useContext
solve?
A: It removes the need for prop drilling by allowing values to be shared across the component tree.
Q2. What happens if a component using useContext
is not wrapped with a Provider?
A: It will use the default value provided when creating the context.
Q3. Can we have multiple contexts in one component?
A: Yes, you can call useContext
multiple times for different contexts.
Q4. Does updating context re-render all consumers?
A: Yes, all components that consume that context will re-render when the value changes.
Q5. When should you avoid using useContext
?
A: For frequently changing state (like input values), since it causes all consumers to re-render. Use local state or state libraries instead.
Q6. Difference between useContext
and Redux?
useContext
: Good for small to medium global state, simple to use, built-in.- Redux: More powerful for complex state, debugging, middlewares, time-travel debugging.