React useEffect Hook
In React, functional components do not have lifecycle methods like class components (componentDidMount, componentDidUpdate, componentWillUnmount). Instead, React provides the useEffect hook, which allows you to perform side effects in function components.
Side effects can include:
- Fetching data from an API
- Subscribing to events
- Manipulating the DOM
- Setting timers
- Logging or analytics
What is useEffect?
The useEffect hook allows you to run code after React renders the component. It combines the functionality of multiple lifecycle methods into a single API for functional components.
Syntax:
useEffect(() => {
// Your side effect code here
return () => {
// Optional cleanup code here
};
}, [dependencies]);
- The first argument is a function containing the effect.
- The return function is optional and is used to clean up resources when the component unmounts or before the effect runs again.
- The second argument is an array of dependencies that determine when the effect should run.
Example – Logging
import React, { useEffect } from "react";
function Logger() {
useEffect(() => {
console.log("Component rendered or updated!");
});
return <h1>Hello, React!</h1>;
}
export default Logger;
useEffect with Empty Dependency Array – Component Did Mount
If you want the effect to run only once (when the component mounts):
import React, { useEffect } from "react";
function App() {
useEffect(() => {
console.log("Component mounted!");
// Cleanup on unmount
return () => console.log("Component will unmount!");
}, []); // Empty array = run only on mount
return <h1>Hello, React!</h1>;
}
export default App;
useEffect with Dependencies – Component Did Update
When you want the effect to run only when certain variables change, include them in the dependency array:
import React, { useState, useEffect } from "react";
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`Count changed: ${count}`);
}, [count]); // Effect runs only when `count` changes
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default Counter;
Cleanup Function – Cleaning Side Effects
The cleanup function prevents memory leaks by removing subscriptions, timers, or event listeners.
- Cleanup is called before unmount or before the next effect execution if dependencies change.
import React, { useEffect } from "react";
function Timer() {
useEffect(() => {
const interval = setInterval(() => {
console.log("Tick");
}, 1000);
return () => {
clearInterval(interval); // Clean up interval on unmount
console.log("Timer cleared");
};
}, []);
return <h1>Timer is running...</h1>;
}
export default Timer;
Common useEffect Patterns
Fetching API Data
useEffect(() => {
async function fetchData() {
const response = await fetch("<https://api.example.com/data>");
const data = await response.json();
console.log(data);
}
fetchData();
}, []); // Runs once on mount
Listening to Events
useEffect(() => {
function handleResize() {
console.log("Window resized!");
}
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []); // Runs once on mount
Updating Document Title
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]); // Runs whenever count changes
Rules of useEffect
- Always call at the top level of the component (not inside loops or conditions).
- Do not call conditionally inside blocks; use dependencies instead.
- Dependencies must include all variables used in the effect to avoid stale values.
Why useEffect is Important?
- Handles side effects in functional components
- Combines mounting, updating, and unmounting behavior in a single hook
- Prevents memory leaks with cleanup functions
- Works with other hooks like
useStateanduseReducerfor dynamic UI
