useEffect Hook
Introduction
React is a powerful JavaScript library for building user interfaces, and hooks have transformed the way developers write React components. One of the most essential hooks is useEffect, which is used to handle side effects in functional components.
In this article, we will explore:
✅ What useEffect is
✅ Why it is needed
✅ How to use it properly
✅ Real-world examples of side effects
✅ Best practices for using useEffect efficiently
By the end of this guide, you will have a solid understanding of how to handle side effects in React applications using the useEffect hook.
What Are React Hooks? A Beginner’s Guide
What is useEffect Hook?
useEffect is a built-in React hook that allows you to perform side effects in function components. Side effects include:
- Fetching data from an API
- Updating the DOM
- Subscribing to events
- Setting up and cleaning up timers
Before hooks were introduced, class components used lifecycle methods like componentDidMount
, componentDidUpdate
, and componentWillUnmount
to handle side effects. With hooks, we can achieve the same functionality in a much cleaner way using useEffect.
Why Do We Need useEffect?
In functional components, we do not have lifecycle methods like in class components. However, we often need to perform actions after rendering, such as fetching data or updating the page title.
This is where useEffect comes in. It allows us to:
- Perform side effects after rendering
- Specify dependencies to control when the effect runs
- Clean up resources when a component unmounts
Without useEffect, handling side effects in functional components would be very difficult and messy.
Basic Syntax of useEffect Hook
The syntax of useEffect is simple:
useEffect(() => {
// Code for side effect
}, [dependencies]);
Parameters of useEffect
1️⃣ Callback Function: This is where you write the code for the side effect.
2️⃣ Dependency Array: This controls when the effect runs. If empty ([]
), it runs only once.
How useEffect Works: Examples
Let’s go through different use cases of useEffect with examples.
1. Running useEffect After Every Render
By default, useEffect runs after every render.
import React, { useState, useEffect } from "react";
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("Component rendered!");
});
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default Counter;
🔹 In this example, the effect runs after every re-render. Every time the button is clicked, the console logs “Component rendered!”.
2. Running useEffect Only Once (ComponentDidMount Equivalent)
To run the effect only once when the component mounts, pass an empty dependency array ([]
).
useEffect(() => {
console.log("Component mounted!");
}, []);
Example:
import React, { useEffect } from "react";
function WelcomeMessage() {
useEffect(() => {
alert("Welcome to the page!");
}, []);
return <h1>Hello, User!</h1>;
}
export default WelcomeMessage;
🔹 The alert will pop up only once when the component mounts.
3. Running useEffect When State or Props Change
To run an effect when a specific state or prop changes, include it in the dependency array.
Example:
import React, { useState, useEffect } from "react";
function ShowMessage() {
const [message, setMessage] = useState("Hello!");
useEffect(() => {
console.log("Message changed:", message);
}, [message]);
return (
<div>
<h1>{message}</h1>
<button onClick={() => setMessage("Hello, React!")}>Update Message</button>
</div>
);
}
export default ShowMessage;
🔹 The console will log the new message only when message
changes.
4. Cleaning Up Effects (ComponentWillUnmount Equivalent)
Some side effects (like event listeners and timers) require cleanup to prevent memory leaks. useEffect can return a cleanup function.
Example:
import React, { useState, useEffect } from "react";
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount((prev) => prev + 1);
}, 1000);
return () => {
clearInterval(interval); // Cleanup
};
}, []);
return <h1>Timer: {count}</h1>;
}
export default Timer;
🔹 The timer starts when the component mounts and stops when the component unmounts.
Common useEffect Use Cases
✅ Fetching API Data
import React, { useState, useEffect } from "react";
function FetchData() {
const [data, setData] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/posts")
.then((response) => response.json())
.then((json) => setData(json));
}, []);
return (
<ul>
{data.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
export default FetchData;
✅ Updating the Document Title
import React, { useState, useEffect } from "react";
function PageTitle() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return (
<button onClick={() => setCount(count + 1)}>Increase Count</button>
);
}
export default PageTitle;
✅ Event Listeners
useEffect(() => {
const handleResize = () => {
console.log("Window resized!");
};
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
Best Practices for useEffect
✔️ Avoid Unnecessary Re-renders
Always provide a dependency array to avoid unnecessary re-execution.
✔️ Use Cleanup Functions
Always clean up effects like timers, subscriptions, and event listeners.
✔️ Group Related Effects
If multiple effects handle the same logic, combine them into one useEffect.
✔️ Use Dependencies Carefully
Only include necessary dependencies to prevent infinite loops.
Conclusion
The useEffect hook is one of the most important hooks in React, allowing developers to handle side effects efficiently. Whether you need to fetch data, update the DOM, or manage timers, useEffect provides a clean and effective way to do it.
By following best practices, you can ensure that your React applications remain efficient and performant. Now that you understand useEffect, start experimenting with it in your projects! 🚀