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
useEffect
hooks 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.