-
ReactJS useEffect Hook
-
In React, components are responsible for rendering UI. However, real applications often need to perform tasks outside rendering, such as fetching data, updating the document title, API calls, setting up timers, or listening to events. These tasks are called side effects.
The
useEffectHook is React’s official way to handle side effects inside functional components.This article explains what useEffect is, why it is required, how it works, and how to use it correctly, with clear explanations and practical examples.
What is useEffect?
Side effects include:useEffectis a React Hook that allows you to run side-effects logic in functional components.- API calls
- Timers (setTimeout, setInterval)
- DOM updates
- Event listeners
- Logging
- Subscriptions
Syntax of useEffect
useEffect(() => { // side effect logic }, [dependencies]);
Why Do We Need useEffect?
React components re-render whenever state or props change.
However, some logic should not run during rendering, such as:- Fetching data from a server
- Updating the browser title
- Starting or cleaning up timers
componentDidMountcomponentDidUpdatecomponentWillUnmount
useEffectreplaces all of these in functional components.
Importing useEffect (Required)
To use
useEffect, it must be imported from React.
If your component also uses state:import { useEffect } from "react";import { useState, useEffect } from "react";
When does useEffect Run?
This depends entirely on the dependency array.
There are three common patterns :- Without Dependency Array
- With Empty Dependency Array
- With Dependencies
It is completely controlled by the dependency array.
Lets understand each of these with examples:
1. Without Dependency Array (Runs Every Render):
When you don’t pass a dependency array, the useEffect runs after every render.
Explanation:import { useEffect } from "react"; function Example() { useEffect(() => { console.log("Component rendered"); }); return ( <div>Hello</div> ); } export default Example;In Without Dependency Array case, useEffect runs after every render and even runs after every state or prop change.
Usage:- Rare
- Debugging
- Logging render cycles
2. With Empty Dependency Array (Runs Once)An empty dependency array tells React to run the effect only once, after the first render.
import { useEffect } from "react"; function Example() { useEffect(() => { console.log("Component mounted"); }, []); return ( <div>Hello</div> ); } export default Example;This behaves like componentDidMount in class components.
When to use it:- API calls on page load
- Initial setup logic
- Event listeners
- Timers or subscriptions
import { useState, useEffect } from "react"; function Users() { const [users, setUsers] = useState([]); useEffect(() => { fetch("https://jsonplaceholder.typicode.com/users") .then(res => res.json()) .then(data => setUsers(data)); }, []); return ( <ul> {users.map((user) => ( <li key={user.id}> {user.name} </li> ))} </ul> ); } export default Users;
3. useEffect With Dependencies (Runs on Change)The effect runs:
- On first render
- Again only when specified dependencies change
Why this is powerful?import { useState, useEffect } from "react"; function Counter() { const [count, setCount] = useState(0); useEffect(() => { console.log("Count changed:", count); }, [count]); return ( <button onClick={() => setCount(count + 1)}> Count: {count} </button> ); } export default Counter;- Prevents unnecessary executions
- Improves performance
- Keeps logic predictable and controlled
Cleaning Up Effects (Very Important)
Cleanup in
Some effects which require cleanup, such as:useEffectremoves side effects when the component no longer needs them. Most importantly, cleanup prevents memory leaks.- Notifications
- Event listeners
- Success / error banners
- Auto-dismiss alerts
import React from 'react'; import { useState, useEffect } from 'react'; function App() { const [show, setShow] = useState(true); useEffect(() => { const timeoutId = setTimeout(() => { setShow(false); }, 3000); // cleanup return () => { clearTimeout(timeoutId); }; }, []); if (!show) return <h3>Hide it!</h3>; return <h3>Saved successfully!</h3>; } export default App;
useEffect vs Lifecycle Methods (Conceptual)
Class Lifecycle useEffect Equivalent Explanation componentDidMount useEffect(() => { ... }, [])Runs once after the component mounts. Commonly used for API calls, subscriptions, or initial setup logic. componentDidUpdate useEffect(() => { ... }, [dependencies])Executes whenever specified state or props change. Useful for reacting to updates such as data refresh or DOM updates. componentWillUnmount Cleanup function inside useEffectRuns just before the component is removed. Used to clean up timers, event listeners, or subscriptions.
Final Summary
The
useEffectHook allows functional components to perform side effects safely and predictably. It runs after rendering, reacts to state and prop changes, and provides a built-in cleanup mechanism for managing resources.By controlling when effects run using the dependency array, developers can replace class lifecycle methods with a single, flexible API. Whether it’s fetching data, synchronizing state, or managing timers, useEffect is essential for building real-world React applications.
Mastering useEffect is a key milestone in modern React development and sets the stage for advanced Hooks like useReducer, useContext, and performance optimization techniques.