useEffect is used to synchronize a component with something outside normal rendering. Common examples are API requests, timers, subscriptions, browser events, and external systems.
1 What Is a Side Effect?
Rendering should calculate the UI. A side effect performs work involving something outside that calculation.
- API calls
- Timers
- Subscriptions
- Browser event listeners
2 Basic Syntax
useEffect(() => {
console.log("Effect executed");
});
Without dependencies, the effect can run after every render.
3 Dependency Array
useEffect(() => {
console.log("User:", userId);
}, [userId]);The effect is synchronized again when userId changes.
4 Effect Flow
flowchart TD
A[Component Renders] --> B[React Commits UI]
B --> C[Effect Runs]
C --> D[External Work]
D --> E[State May Update]
E --> A
5 API Call Example
function Users() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
useEffect(() => {
async function loadUsers() {
try {
const response = await fetch("/api/users");
if (!response.ok) throw new Error("Unable to load users");
setUsers(await response.json());
} catch (error) {
setError(error.message);
} finally {
setLoading(false);
}
}
loadUsers();
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>{error}</p>;
return <ul>{users.map(user => <li key={user.id}>{user.name}</li>)}</ul>;
}
6 Cleanup
useEffect(() => {
const timerId = setInterval(() => console.log("Tick"), 1000);
return () => clearInterval(timerId);
}, []);
Cleanup is important for timers, subscriptions, and event listeners.
7 API Race Conditions
If an old request finishes after a newer request, stale data can overwrite the latest result. AbortController can cancel the old request.
useEffect(() => {
const controller = new AbortController();
async function loadUser() {
try {
const response = await fetch(`/api/users/${userId}`, {
signal: controller.signal
});
if (!response.ok) throw new Error("Request failed");
setUser(await response.json());
} catch (error) {
if (error.name !== "AbortError") setError(error.message);
}
}
loadUser();
return () => controller.abort();
}, [userId]);
8 When Not to Use useEffect
Do not use an effect for a value that can be calculated directly from props or state.
const total = price * quantity;
9 Common Mistakes
- Missing dependencies.
- Using effects for simple calculations.
- Forgetting cleanup.
- Ignoring API race conditions.
- Creating effects that continuously update their own dependencies.
10 Interview Questions
- What is useEffect?
- What is a side effect?
- What does the dependency array do?
- Why is cleanup needed?
- How do you call an API using useEffect?
- How can API race conditions be prevented?
- When should useEffect not be used?
11 Key Takeaways
- useEffect synchronizes React with external systems.
- Dependencies control synchronization.
- Cleanup removes old external work.
- API calls are a common use case.
Conclusion
The best way to understand useEffect is as a synchronization mechanism for external systems rather than simply as a lifecycle replacement.