React is one of the most popular JavaScript libraries for building user interfaces. If you are learning React, sooner or later you will come across a hook called useEffect. Many beginners find useEffect confusing at first especially when people start talking about side effects, API calls, and dependency arrays.
In this article, we will explain everything step by step :
- What is
useEffectand why it is used - What are side effects in React
- How to make API calls using
useEffect. Usingfetch, Usingaxios - What is the dependency array and how it works
- Common mistakes and best practices
By the end of this post, you will have a clear mental model of how useEffect works and when to use it.
What is useEffect in React?
useEffect is a React Hook that allows you to run some code after a component renders.
In simple words, useEffect lets you perform actions in a React component that are not directly related to rendering UI.
It was introduced in React to replace lifecycle methods used in class components, such as:
componentDidMountcomponentDidUpdatecomponentWillUnmount
With useEffect, you can handle all of these scenarios in functional components.
Why do we need useEffect?
React components mainly do one thing ie take data (state/props) and show UI. But in real applications, we also need to do things like fetch data from an API, update the document title, add or remove event listeners, set timers or intervals, read or write to localStorage. These actions are outside the normal rendering flow. That is why React provides useEffect.
Basic syntax of useEffect
import { useEffect } from "react";
useEffect(() => {
// code to run
});This function runs after every render by default.
What are Side Effects in React?
A side effect is anything your component does that affects something outside the component or depends on something outside the render process.
Examples of side effects
Here are some common side effects in React:
- Making an API call
- Updating
document.title - Reading from localStorage
- Writing to localStorage
- Subscribing to events (scroll, resize)
- Using
setTimeoutorsetInterval
Rendering UI is not a side effect. Fetching data or changing browser behavior is a side effect.
Why are they called “side effects”?
React expects rendering to be:
- Predictable
- Pure
- Free from external changes
Side effects happen on the side, not during rendering. That is why React separates them using useEffect.
Example: Side effect without useEffect (WRONG way)
function MyComponent() {
document.title = "My App";
return <h1>Hello</h1>;
}This works, but it is not recommended. Side effects should be handled inside useEffect.
Correct way using useEffect
function MyComponent() {
useEffect(() => {
document.title = "My App";
}, []);
return <h1>Hello</h1>;
}Now the side effect is clearly defined and controlled.
API Calls in React using useEffect
One of the most common uses of useEffect is fetching data from an API.
Why?
Because API calls are side effects:
- They depend on external servers
- They update component state after response
API call using fetch
Example: Fetching users from an API
import { useEffect, useState } from "react";
function Users() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch("https://phpguruacademy.com/users")
.then(response => response.json())
.then(data => setUsers(data));
}, []);
return (
<div>
<h2>User List</h2>
{users.map(user => (
<p key={user.id}>{user.name}</p>
))}
</div>
);
}Explanation step by step
- Component renders
useEffectruns after renderfetchcalls the API- API returns data
setUsersupdates state- Component re-renders with data
Why API calls should be inside useEffect
If you call API directly inside the component body:
fetch("url");It will run on every render, causing:
- Infinite loops
- Performance issues
- Multiple API requests
useEffect helps control when the API call runs.
API calls using async/await with fetch
useEffect(() => {
async function fetchUsers() {
const response = await fetch("https://phpguruacademy.com/users");
const data = await response.json();
setUsers(data);
}
fetchUsers();
}, []);This version is cleaner and easier to read.
API Calls using Axios
What is Axios?
Axios is a popular library for making HTTP requests. Many developers prefer Axios because:
- Cleaner syntax
- Automatic JSON parsing
- Better error handling
Installing Axios
npm install axiosExample: API call using Axios
import axios from "axios";
import { useEffect, useState } from "react";
function Posts() {
const [posts, setPosts] = useState([]);
useEffect(() => {
axios.get("https://phpguruacademy.com/posts")
.then(response => {
setPosts(response.data);
});
}, []);
return (
<div>
<h2>Posts</h2>
{posts.map(post => (
<p key={post.id}>{post.title}</p>
))}
</div>
);
}Axios with async/await
useEffect(() => {
async function fetchPosts() {
const response = await axios.get("https://phpguruacademy.com/posts");
setPosts(response.data);
}
fetchPosts();
}, []);Handling errors in API calls
useEffect(() => {
async function fetchData() {
try {
const response = await axios.get("url");
setData(response.data);
} catch (error) {
console.error("Error fetching data", error);
}
}
fetchData();
}, []);Always handle errors to avoid broken UI.
What is the Dependency Array in useEffect?
Now comes the most confusing part for beginners, the dependency array .
Syntax
useEffect(() => {
// side effect
}, [dependencies]);The dependency array controls when the effect should run.
Case 1: No dependency array
useEffect(() => {
console.log("Runs every render");
});- Runs after every render
- Usually not recommended
Case 2: Empty dependency array []
useEffect(() => {
console.log("Runs once");
}, []);- Runs only once
- Similar to
componentDidMount - Best for: API calls and Initial setup
Case 3: With dependencies
useEffect(() => {
console.log("Runs when count changes");
}, [count]);- Runs when
countchanges - Also runs once on initial render
Example: API call based on state change
useEffect(() => {
fetch(`https://api.example.com/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);Whenever userId changes, the API is called again.
Why dependency array is important
Without correct dependencies:
- Effects may run too often
- Effects may not run when needed
- Bugs become hard to detect
React’s ESLint warning often tells you “Add dependency to dependency array”. This is React helping you avoid bugs.
Cleanup function in useEffect
Some side effects need cleanup:
- Event listeners
- Timers
- Subscriptions
Example
useEffect(() => {
const timer = setInterval(() => {
console.log("Running...");
}, 1000);
return () => {
clearInterval(timer);
};
}, []);Cleanup runs when:
- Component unmounts
- Dependencies change
Common mistakes with useEffect:
- Infinite loop
useEffect(() => {
setCount(count + 1);
}, [count]);This causes infinite re-rendering.
- Missing dependency
useEffect(() => {
console.log(name);
}, []); // name missingAlways include used variables in dependency array.
Best practices for useEffect
- Keep effects small
- One effect = one responsibility
- Use dependency array correctly
- Avoid unnecessary effects
- Prefer
useEffectfor side effects only
Summary
useEffectis used to handle side effects in React- Side effects include API calls, browser updates, subscriptions
- API calls should always be inside
useEffect fetchandaxiosboth work well- Dependency array controls when the effect runs
- Proper usage prevents bugs and performance issues
If you understand when your effect should run and why, useEffect becomes simple and powerful. Practice with small examples, and soon it will feel natural.
