The useEffect hook in React lets you perform side effects in functional components, such as data fetching, setting up subscriptions, or manually updating the DOM. It is a powerful and essential tool to manage lifecycle events in React’s functional components.
Syntax
useEffect(() => {
// Side effect code here
return () => {
// Cleanup code here (optional)
};
}, [dependencies]);
- First argument: A function that contains the side effect logic.
- Second argument (
dependencies): An array that determines when the effect runs. It controls re-execution based on state or prop changes.
When to Use useEffect
- Fetching data (e.g., API calls).
- Setting up subscriptions (e.g., WebSocket or event listeners).
- Updating the DOM (e.g., document title).
- Cleaning up resources (e.g., clearing timers or subscriptions).
Basic Example: Without Dependencies
The effect runs after every render.
import React, { useState, useEffect } from "react";
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`Count updated: ${count}`);
});
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default Counter;
Using Dependencies
The effect runs only when specific dependencies change.
useEffect(() => {
// Code runs only when "count" changes
}, [count]);
Example:
import React, { useState, useEffect } from "react";
function Message() {
const [message, setMessage] = useState("");
useEffect(() => {
console.log("Message changed:", message);
}, [message]);
return (
<div>
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<p>{message}</p>
</div>
);
}
export default Message;
Using useEffect for Data Fetching
import React, { useState, useEffect } from "react";
function UsersList() {
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 after the initial render.
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default UsersList;
Cleanup in useEffect
If the effect involves resources that need to be released (e.g., subscriptions or timers), use the cleanup function.
useEffect(() => {
const timer = setInterval(() => {
console.log("Timer running...");
}, 1000);
// Cleanup the timer when the component unmounts
return () => {
clearInterval(timer);
};
}, []); // Empty dependency array ensures the cleanup is tied to unmount.
Multiple useEffect Hooks
You can use multiple useEffect hooks to separate concerns.
import React, { useState, useEffect } from "react";
function MultipleEffects() {
const [count, setCount] = useState(0);
const [message, setMessage] = useState("");
useEffect(() => {
console.log(`Count: ${count}`);
}, [count]);
useEffect(() => {
console.log(`Message: ${message}`);
}, [message]);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment Count</button>
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
</div>
);
}
export default MultipleEffects;
Common Scenarios
1. Run Effect Only Once
Add an empty dependency array ([]) to run the effect only after the first render.
useEffect(() => {
console.log("Component mounted");
}, []); // Runs only once.
2. Run Effect on State/Prop Change
Specify the state/prop in the dependency array to trigger the effect when it changes.
useEffect(() => {
console.log("Dependency changed");
}, [dependency]); // Runs whenever "dependency" changes.
3. Cleanup on Component Unmount
Return a cleanup function to handle unmount logic.
useEffect(() => {
const interval = setInterval(() => {
console.log("Running...");
}, 1000);
return () => {
clearInterval(interval); // Cleanup when the component unmounts
};
}, []);
Key Points to Remember
- Avoid unnecessary renders:
- Use dependencies wisely to avoid infinite loops or redundant effects.
- Keep effects clean:
- Perform only the necessary side effects inside
useEffect.
- Perform only the necessary side effects inside
- Handle asynchronous tasks properly:
- Always manage cleanup when dealing with asynchronous calls (e.g., canceling fetch requests or timers).
- Separate concerns:
- Use multiple
useEffecthooks for different responsibilities instead of combining all logic in one.
- Use multiple
Best Practices
- Always specify dependencies explicitly.
- Use cleanup functions to avoid memory leaks.
- Avoid directly mutating states or props inside
useEffect.
useEffect is an essential hook for managing side effects in React.