If you want to use API calls, DOM updates, or timers in React, then mastering the useEffect hook is a must.
In this post, you’ll learn about the useEffect hook to handle side effects efficiently.
Before we get started, don’t forget to subscribe to my newsletter!
Get the latest tips, tools, and resources to level up your web development skills delivered straight to your inbox. Subscribe here!
Now let’s jump right into it!🚀
What is useEffect?
useEffect
is a React Hook used to handle side effects in functional components.
Side effects are operations that occur after a component renders, such as:
- API calls (fetching data)
- DOM Manipulation (Title change, Event listeners)
- Saving data in local storage
- Timers (setTimeout, setInterval)
Before Hooks, class components used componentDidMount
, componentDidUpdate
, and componentWillUnmount
for handling side effects. useEffect
replaces all three in functional components.
Why You Need useEffect?
React follows a pure rendering approach, which means the UI should be predictable and free of side effects.
If you directly place API calls or DOM updates inside the component’s rendering, then you might face issues like:
- Unexpected behavior: React may re-run the function multiple times, causing inconsistent updates.
- Performance issues: Heavy operations (like API calls) running on every render can slow down the app.
- Memory leaks: Timers and event listeners may keep running even after a component is removed.
To avoid these issues, React provides the useEffect
hook. It ensures that side effects run after rendering, keeping the UI updates smooth and efficient.
Basic Syntax of useEffect
Before using useEffect
, you need to import it from React:
import { useEffect } from "react";
Syntax:
useEffect(() => {
// Side effect logic
}, [dependencies]);
Here,
- First argument: A function that executes the side effect.
- Second argument (dependency array): Defines when the effect should run.
Simple Example of useEffect (Title Update)
import { useState, useEffect } from "react";
function TitleUpdater() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
});
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
In this example, whenever the count
updates, the title of the document will change.
Controlling When useEffect Runs
Empty Dependency Array ([ ])
useEffect(() => {
console.log("Component Mounted!");
}, []);
This will only run when the component is rendered for the first time. (like componentDidMount
in class components).
Specific Dependency
If you want to run useEffect
only when specific values change, then it’s necessary to provide the dependency array.
useEffect(() => {
console.log(`Count changed to ${count}`);
}, [count]);
This will run only when the count
gets changed.
No Dependency
useEffect(() => {
console.log("Component Re-rendered!");
});
This will run after each render (not recommended unless necessary).
Fetching Data with useEffect
useEffect
is commonly used for fetching data from an API.
import { useState, useEffect } from "react";
function FetchData() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then(response => response.json())
.then(data => setUsers(data));
}, []); // Empty dependency array: Runs only once when the component mounts
return (
<div>
<h1>User List</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
API will be called only once when the component mounts. This is an efficient approach.
Cleanup Function (Avoiding Memory Leak)
If your side effects involve event listeners or timers, always clean up to prevent memory leaks.
Cleanup in setTimeout / setInterval
useEffect(() => {
const timer = setInterval(() => {
console.log("Interval Running...");
}, 1000);
return () => {
clearInterval(timer); // Cleanup
};
}, []);
The interval will be cleared when the component unmounts.
Cleanup in Event Listeners
useEffect(() => {
const handleResize = () => console.log("Window Resized!");
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
On Component unmount, the resize event listener will be removed, and this will prevent unnecessary executions.
Common Mistakes & Best Practices
Mistake 1
Mistake: Not providing a dependency array
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then(response => response.json())
.then(data => console.log(data));
}); // No dependency array → Infinite API calls
Solution: Provide an empty dependency array ([])
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then(response => response.json())
.then(data => console.log(data));
}, []); // API will be called only once
Mistake 2
Mistake: Not using a cleanup function
useEffect(() => {
setInterval(() => {
console.log("Running...");
}, 1000);
}, []); // This will cause memory leak, if you don't provide clean up function
Solution: Use the cleanup function
useEffect(() => {
const timer = setInterval(() => {
console.log("Running...");
}, 1000);
return () => clearInterval(timer); // Cleanup function!
}, []);
When to Use useEffect?
- When you need to handle browser events (resize, scroll, etc.).
- When you need to call APIs or fetch data.
- When you need to update the DOM (title change, event listeners).
- When you need to manage local storage or authentication.
🎯Wrapping Up
That’s all for today!
For paid collaboration connect with me at : [email protected]
I hope this post helps you.
If you found this post helpful, here’s how you can support my work:
☕ Buy me a coffee – Every little contribution keeps me motivated!
📩 Subscribe to my newsletter – Get the latest tech tips, tools & resources.
𝕏 Follow me on X (Twitter) – I share daily web development tips & insights.
Keep coding & happy learning!