useEffect β Side Effects and Lifecycle
After completing this topic, you will:
Understand when useEffect runs, what a dependency array is, and why cleanup functions are necessary.
What are Side Effects?
The main role of a React component is to receive state and render UI. However, other tasks are also required:
- Fetching data from an API
- Setting timers
- Modifying the document title
- Registering event listeners
These tasks are called side effects. useEffect is a Hook that handles these side effects.
Basic Usage
import { useEffect, useState } from "react";
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);
if (!user) return <p>Loading...</p>;
return <h1>{user.name}</h1>;
}useEffect takes two arguments:
- A function to execute (the effect)
- A dependency array (when to re-run)
Dependency Array
The execution time depends on the dependency array:
// Runs on every render (no dependency array)
useEffect(() => {
console.log("Runs on every render");
});
// Runs only once on mount (empty array)
useEffect(() => {
console.log("Runs only once");
}, []);
// Runs whenever `userId` changes
useEffect(() => {
fetchUser(userId);
}, [userId]);React compares the values in the dependency array with the previous render. If the values are different, the effect is re-executed.
Cleanup Function
useEffect(() => {
const timer = setInterval(() => {
console.log("tick");
}, 1000);
return () => {
clearInterval(timer);
};
}, []);If the effect function returns a function, that is the cleanup function. It is executed in the following situations:
- When the component unmounts (disappears from the screen)
- Just before the effect is re-executed (when the dependency changes)
It is used for cleanup tasks such as clearing timers, removing event listeners, and unsubscribing.
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);Common Mistake: Infinite Loop
// Infinite loop!
useEffect(() => {
setCount(count + 1);
}); // No dependency array β runs on every render β setState β re-render β runs againIf you modify state within the effect and there is no dependency array, the effect will run again after each re-render.
// Correct way
useEffect(() => {
fetchData().then(setData);
}, []); // Empty array β runs only onceKey Takeaways
useEffectexecutes side effects after rendering. The dependency array, if empty, runs only once on mount; if populated, it runs when the values change. Use the cleanup function to clean up timers, listeners, etc., to prevent memory leaks.