Side Effects with useEffect
useEffect runs code after render to handle side effects.
Basic Syntax
import { useEffect } from "react";
function App() {
useEffect(() => {
console.log("Component rendered");
});
return <div>Hello</div>;
}
The function runs after every render by default.
Dependency Array
Control when the effect runs:
useEffect(() => {
console.log("Count changed:", count);
}, [count]);
- Empty
[]— runs once on mount [a, b]— runs whenaorbchanges- No array — runs after every render
Cleanup Function
Return a function to clean up resources:
useEffect(() => {
const id = setInterval(() => console.log("tick"), 1000);
return () => clearInterval(id);
}, []);
Always clean up subscriptions and timers.
Fetching Data
useEffect(() => {
async function fetchData() {
const res = await fetch("/api/users");
const data = await res.json();
setUsers(data);
}
fetchData();
}, []);
Call the async function inside the effect, not directly.
Common Pitfalls
// Infinite loop — mutating state inside effect
useEffect(() => {
setCount(count + 1);
}, [count]); // keeps triggering
Best Practices
- Always specify a dependency array
- Clean up intervals, subscriptions, and event listeners
- Use async functions inside effects properly
- Avoid effects that just set state — derive instead
Common Mistakes
- Missing dependencies causes stale values
- No cleanup causes memory leaks
- Calling async functions directly in useEffect
- Updating state without a condition leads to infinite loops