React’s useEffect Hook provides a way to handle lifecycle events in functional components, replacing the need for lifecycle methods found in class components. It allows developers to execute side effects in different stages of a component’s lifecycle: mounting, updating, and unmounting.
How useEffect Works
The useEffect Hook takes two arguments:
- Effect Function: A function containing the logic for the side effect.
- Dependency Array (optional): A list of values that the effect depends on. React re-runs the effect only when one of these values changes.
useEffect(() => { // Your effect logic here }, [dependencies]);
Mapping useEffect to Lifecycle Events
- Mounting: When the component is first rendered.
- Updating: When the component’s state or props change.
- Unmounting: When the component is removed from the DOM.
1. Mounting (Equivalent to componentDidMount)
To run an effect only once after the component mounts, provide an empty dependency array [].
import React, { useEffect } from "react";
const App = () => {
useEffect(() => {
console.log("Component mounted");
// Example: Fetch data
fetch("/api/data")
.then(response => response.json())
.then(data => console.log(data));
}, []); // Empty dependency array ensures this runs only once
return <h1>Hello, React!</h1>;
};
export default App;
2. Updating (Equivalent to componentDidUpdate)
To run an effect when specific dependencies change, include those dependencies in the array.
import React, { useState, useEffect } from "react";
const Counter = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`Count updated: ${count}`);
}, [count]); // Runs only when 'count' changes
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
export default Counter;
3. Unmounting (Equivalent to componentWillUnmount)
To clean up resources when a component unmounts, return a cleanup function from the useEffect.
import React, { useEffect } from "react";
const Timer = () => {
useEffect(() => {
const interval = setInterval(() => {
console.log("Tick");
}, 1000);
// Cleanup function
return () => {
clearInterval(interval);
console.log("Component unmounted");
};
}, []); // Empty dependency array ensures this runs once
return <h1>Timer is running...</h1>;
};
export default Timer;
Handling All Lifecycle Stages
useEffect can handle mounting, updating, and unmounting in a single declaration.
import React, { useState, useEffect } from "react";
const LifecycleDemo = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("Mounting or updating...");
return () => {
console.log("Cleanup before unmount or re-run of effect");
};
}, [count]); // Runs on mount and every time 'count' changes
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
export default LifecycleDemo;
Best Practices for Using useEffect
- Manage Dependencies Carefully:
- Always specify dependencies in the array to avoid unnecessary re-renders or missed updates.
- Use the
eslint-plugin-react-hooksrule to catch common dependency issues.
- Avoid Heavy Computations:
- Use memoization (
useMemooruseCallback) to prevent re-executing expensive operations.
- Use memoization (
- Separate Concerns:
- Avoid putting too much logic in a single
useEffect. - Use multiple
useEffectcalls to separate concerns, e.g., data fetching, subscriptions, or event listeners.useEffect(() => { // Data fetching logic }, [dataDependency]); useEffect(() => { // Event listener logic }, []);
- Avoid putting too much logic in a single
- Cleanup Properly:
- Always return a cleanup function to avoid memory leaks (e.g., remove event listeners, cancel subscriptions).
- Use Custom Hooks:
- Abstract common logic into reusable custom hooks for cleaner code.
Comparison: Class vs. Functional Components
| Lifecycle Event | Class Component | Functional Component (useEffect) |
|---|---|---|
| Component Mount | componentDidMount |
useEffect(() => { ... }, []) |
| Component Update | componentDidUpdate |
useEffect(() => { ... }, [dependencies]) |
| Component Unmount | componentWillUnmount |
useEffect(() => { return () => { ... }; }, []) |