π React `useContext` Hook Explained (Beginner β Deep β Production)
π React `useContext` Hook Explained (Beginner β Deep β Production) This guide explains **useContext** clearly and deeply. We will understand: - What problem useContext solves - What is Context API - Why prop drilling is bad - How useContext works - Internal mental model - Performance considerations - Memory leak considerations - Real production patterns --- # πΉ 1οΈβ£ The Problem: Prop Drilling Imagine: ```jsx <App> <Layout> <Navbar> <UserProfile /> </Navbar> </Layout> </App> ```
π React `useContext` Hook Explained (Beginner β Deep β Production)
This guide explains **useContext** clearly and deeply.
We will understand:
- What problem useContext solves
- What is Context API
- Why prop drilling is bad
- How useContext works
- Internal mental model
- Performance considerations
- Memory leak considerations
- Real production patterns
- What is Context API
- Why prop drilling is bad
- How useContext works
- Internal mental model
- Performance considerations
- Memory leak considerations
- Real production patterns
---
# πΉ 1οΈβ£ The Problem: Prop Drilling
Imagine:
```jsx
<App>
<Layout>
<Navbar>
<UserProfile />
</Navbar>
</Layout>
</App>
```
<App>
<Layout>
<Navbar>
<UserProfile />
</Navbar>
</Layout>
</App>
```
You want to pass `user` data to `UserProfile`.
Without Context:
```jsx
<App user={user}>
<Layout user={user}>
<Navbar user={user}>
<UserProfile user={user} />
</Navbar>
</Layout>
</App>
```
<App user={user}>
<Layout user={user}>
<Navbar user={user}>
<UserProfile user={user} />
</Navbar>
</Layout>
</App>
```
This is called:
> Prop Drilling
Problems:
β Passing props through unnecessary components
β Hard to maintain
β Bad scalability
β Hard to maintain
β Bad scalability
---
# πΉ 2οΈβ£ What is Context?
Context is:
> A way to share data globally across components without passing props manually at every level.
---
# πΉ 3οΈβ£ Basic Flow of Context
There are 3 steps:
1οΈβ£ Create Context
2οΈβ£ Provide Context
3οΈβ£ Consume Context using `useContext`
2οΈβ£ Provide Context
3οΈβ£ Consume Context using `useContext`
---
# πΉ 4οΈβ£ Step 1 β Create Context
```jsx
import { createContext } from "react";
import { createContext } from "react";
export const UserContext = createContext(null);
```
```
---
# πΉ 5οΈβ£ Step 2 β Provide Context
```jsx
import { UserContext } from "./UserContext";
import { UserContext } from "./UserContext";
function App() {
const user = { name: "Sonu" };
const user = { name: "Sonu" };
return (
<UserContext.Provider value={user}>
<Layout />
</UserContext.Provider>
);
}
```
<UserContext.Provider value={user}>
<Layout />
</UserContext.Provider>
);
}
```
Now all children of `Provider` can access `user`.
---
# πΉ 6οΈβ£ Step 3 β Consume Context
```jsx
import { useContext } from "react";
import { UserContext } from "./UserContext";
import { useContext } from "react";
import { UserContext } from "./UserContext";
function UserProfile() {
const user = useContext(UserContext);
const user = useContext(UserContext);
return <h1>{user.name}</h1>;
}
```
}
```
Now:
β No prop drilling
β Clean structure
β Clean structure
---
# πΉ 7οΈβ£ How `useContext` Works Internally
When React renders:
- Provider stores value in internal tree
- Any component calling `useContext` subscribes to that value
- When value changes β subscribed components re-render
- Any component calling `useContext` subscribes to that value
- When value changes β subscribed components re-render
Important:
> Only components using that context re-render.
---
# πΉ 8οΈβ£ When Context Value Changes
If Provider value changes:
```jsx
<UserContext.Provider value={newUser}>
```
<UserContext.Provider value={newUser}>
```
React:
1οΈβ£ Compares old value
2οΈβ£ Notifies consumers
3οΈβ£ Re-renders them
2οΈβ£ Notifies consumers
3οΈβ£ Re-renders them
---
# πΉ 9οΈβ£ Important Performance Warning
If you pass object directly:
```jsx
<UserContext.Provider value={{ name: "Sonu" }}>
```
<UserContext.Provider value={{ name: "Sonu" }}>
```
Every render creates new object β triggers re-render.
Better:
```jsx
const user = useMemo(() => ({ name: "Sonu" }), []);
<UserContext.Provider value={user}>
```
const user = useMemo(() => ({ name: "Sonu" }), []);
<UserContext.Provider value={user}>
```
---
# πΉ π Context + State (Common Pattern)
```jsx
function App() {
const [theme, setTheme] = useState("light");
function App() {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Layout />
</ThemeContext.Provider>
);
}
```
<ThemeContext.Provider value={{ theme, setTheme }}>
<Layout />
</ThemeContext.Provider>
);
}
```
Consumer:
```jsx
const { theme, setTheme } = useContext(ThemeContext);
```
const { theme, setTheme } = useContext(ThemeContext);
```
---
# πΉ 1οΈβ£1οΈβ£ Does useContext Cause Memory Leak?
No, directly it does NOT.
But problems happen if:
β You store huge objects
β You create new value object every render
β You attach listeners inside provider without cleanup
β You create new value object every render
β You attach listeners inside provider without cleanup
Always:
β Memoize value
β Cleanup effects
β Cleanup effects
---
# πΉ 1οΈβ£2οΈβ£ When NOT to Use Context
β For frequently changing values (like mouse position)
β For deeply performance-sensitive parts
β Instead of proper state management
β For deeply performance-sensitive parts
β Instead of proper state management
Too much context can slow app.
---
# πΉ 1οΈβ£3οΈβ£ Context vs Redux/Zustand
Context:
β Good for theme, auth, language
β Simple global state
β Simple global state
Redux/Zustand:
β Better for complex state
β Better performance control
β Advanced features
β Better performance control
β Advanced features
---
# πΉ 1οΈβ£4οΈβ£ Real Production Example (Auth Context)
```jsx
const AuthContext = createContext(null);
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [user, setUser] = useState(null);
const login = (data) => setUser(data);
const logout = () => setUser(null);
const logout = () => setUser(null);
const value = useMemo(() => ({ user, login, logout }), [user]);
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
```
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
```
Consumer:
```jsx
const { user, login } = useContext(AuthContext);
```
const { user, login } = useContext(AuthContext);
```
Clean and scalable.
---
# πΉ 1οΈβ£5οΈβ£ Common Mistakes
β Forgetting Provider
β Not memoizing value
β Using context for everything
β Creating multiple nested providers unnecessarily
β Not memoizing value
β Using context for everything
β Creating multiple nested providers unnecessarily
---
# πΉ 1οΈβ£6οΈβ£ Performance Optimization Tips
β Use separate contexts for separate concerns
β Memoize value
β Avoid passing large objects
β Avoid unnecessary re-renders
β Memoize value
β Avoid passing large objects
β Avoid unnecessary re-renders
---
# πΉ 1οΈβ£7οΈβ£ Mental Model
Think like this:
```
Provider = Global data box
useContext = Access key
```
Provider = Global data box
useContext = Access key
```
When box changes β users get update.
---
# π― Final Understanding
useContext is:
1οΈβ£ A way to avoid prop drilling
2οΈβ£ A global data sharing mechanism
3οΈβ£ A subscription-based system
4οΈβ£ Must be optimized carefully
2οΈβ£ A global data sharing mechanism
3οΈβ£ A subscription-based system
4οΈβ£ Must be optimized carefully
---
# π One-Line Summary
> useContext allows components to access shared global data without prop drilling, and re-renders consumers when the context value changes.
---
End of Document.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article