React useEffect Infinite Loop

Introduction

The useEffect Hook is one of the most important Hooks in ReactJS. It is used to handle:

  1. API calls
  2. Timers
  3. Event listeners
  4. DOM updates
  5. Side effects

However, beginners often face a very common issue called the useEffect Infinite Loop.

This problem happens when useEffect continuously runs again and again, causing:

  • Endless re-rendering
  • Performance issues
  • Browser freezing
  • Application crashes

Understanding why infinite loops happen in React is extremely important for every React developer.

What is useEffect?

useEffect is a React Hook used to perform side effects in functional components.

Syntax of useEffect


useEffect(() => {
 // Side effect code
}, []);

What is an Infinite Loop in React?

An infinite loop happens when a component continuously re-renders without stopping.

In React, this usually occurs when:

  • State updates trigger re-render
  • Re-render triggers useEffect
  • useEffect updates state again

This cycle repeats forever.

Common Infinite Loop Error

Error Message

Too many re-renders.
React limits the number of renders to prevent an infinite loop.

Example of Infinite Loop

Wrong Example


import { useEffect, useState } from "react";
function App() {
 const [count, setCount] = useState(0);
 useEffect(() => {
   setCount(count + 1);
 });
 return (
   <h1>{count}</h1>
 );
}
export default App;

Why This Causes Infinite Loop?

Step-by-Step

Step 1

Component renders.

Step 2

useEffect runs.

Step 3

setCount() updates state.

Step 4

State update causes re-render.

Step 5

useEffect runs again.

Step 6

Loop continues forever.

Fixing Infinite Loop Using Dependency Array

Correct Example


import { useEffect, useState } from "react";
function App() {
 const [count, setCount] =
   useState(0);
 useEffect(() => {
   setCount(count + 1);
 }, []);
 return (
   <h1>{count}</h1>
 );
}
export default App;

Why This Fix Works?

The empty dependency array:

tells React:

  • Run useEffect only once
  • Run after initial render only

Now infinite loop stops.

Understanding Dependency Array

The dependency array controls when useEffect executes.

Example 1: Empty Dependency Array


useEffect(() => {
}, []);

Behavior

Runs only once after component mounts.

Example 2: Dependency Added


useEffect(() => {
}, [count]);

Behavior

Runs whenever count changes.

Example 3: No Dependency Array


useEffect(() => {
});

Behavior

Runs after every render.

This commonly causes infinite loops.

Another Common Infinite Loop Example

Wrong Example


useEffect(() => {
 fetchData();
});

Why This Happens?

  • Component renders
  • fetchData() updates state
  • State update causes re-render
  • useEffect runs again

Correct Example


useEffect(() => {
 fetchData();
}, [])

Infinite Loop with Objects

Objects and arrays can also cause loops.

Wrong Example


const user = {
 name: "John"
};
useEffect(() => {
 console.log(user);
}, [user]);

Why This Happens?

Objects are recreated on every render.

React thinks dependency changed every time.

Solution Using useMemo


const user = useMemo(() => {
 return {
   name: "John"
 };
}, []);

Infinite Loop with Functions

Functions can also trigger loops.

Wrong Example


const fetchData = () => {
};
useEffect(() => {
 fetchData();
}, [fetchData]);

Why This Happens?

Functions are recreated during every render.

Solution Using useCallback


const fetchData = useCallback(() => {
}, []);

Real-Life Example

Suppose you fetch products from API

Wrong Example


useEffect(() => {
 axios.get(url)
   .then((response) => {
     setProducts(response.data);
   });
});

Problem

Every state update causes an API call again.

This creates:

  • Infinite API requests
  • Heavy server load
  • Slow application

Correct Example


useEffect(() => {
 axios.get(url)
   .then((response) => {
     setProducts(response.data);
   });
}, []);

Common Causes of useEffect Infinite Loop

Cause Problem
Missing dependency array Effect runs every render
Updating state inside effect Triggers re-render
Functions in dependencies Function recreated every render
Objects in dependencies Object reference changes
Arrays in dependencies New array reference each render

Infinite Loop with State Dependency

Example


useEffect(() => {
 setCount(count + 1);
}, [count]);

Why This Loops Forever?

  • count changes
  • useEffect runs
  • setCount changes count again
  • loop repeats

Correct Solution

Use conditions.


useEffect(() => {
 if (count < 5) {
   setCount(count + 1);
 }
}, [count]);

Using Cleanup Functions

Cleanup functions help prevent memory leaks.

Example


useEffect(() => {
 const timer = setInterval(() => {
 }, 1000);
 return () => {
   clearInterval(timer);
 };
}, []);

Why Cleanup Matters?

Without cleanup:

  • Timers continue running
  • Event listeners remain active
  • Memory leaks occur

Difference Between useEffect Behaviors

Syntax Behavior
useEffect(() => {}) Runs every render
useEffect(() => {}, []) Runs once
useEffect(() => {}, [value]) Runs when dependency changes

How React Checks Dependencies?

React uses shallow comparison.

For primitive values:

  • Number
  • String
  • Boolean

comparison works normally.

For:

  • Arrays
  • Functions

reference comparison happens.

Best Practices to Avoid Infinite Loops

1. Always Use Dependency Array Carefully

Incorrect dependencies create loops.
2. Avoid Unnecessary State Updates

Only update state when needed.

3. Use useCallback for Functions

Memoize functions properly

4. Use useMemo for Objects and Arrays

Prevents unnecessary dependency changes.

5. Read ESLint Warnings

React Hook ESLint rules help detect problems.

Common Mistakes

1. Missing Dependency Array

Wrong:


useEffect(() => {
 fetchData();
});

Correct:


useEffect(() => {
 fetchData();
}, []);

2. Updating State Unconditionally

Wrong:


setCount(count + 1);

3. Using Objects Directly in Dependencies

Wrong:


[user]

4. Ignoring ESLint Warnings

React Hook warnings are useful.

useEffect Infinite Loop in API Calls

This is extremely common.

Wrong Example


useEffect(() => {
 fetchUsers();
});

Correct Example


useEffect(() => {
 fetchUsers();
}, []);

Performance Problems Caused by Infinite Loop

Infinite loops can cause:

  • Excessive API calls
  • Browser crashes
  • CPU overuse
  • Memory leaks
  • Poor user experience

Debugging Infinite Loops

Tips

Use Console Logs

console.log(“Rendered”);

Check Dependency Arrays

Verify dependencies carefully.

Use React Developer Tools

Inspect renders and state changes.

Read Error Messages

React usually provides helpful errors.

Real-World Scenario

Imagine an e-commerce website.

If product API continuously calls due to infinite loop:

  • Server gets overloaded
  • Users experience lag
  • Website performance decreases

Fixing dependency arrays prevents this issue.

Advanced Example


import {
 useEffect,
 useState,
 useCallback
} from "react";
function App() {
 const [users, setUsers] =
   useState([]);
 const fetchUsers = useCallback(async () => {
   const response = await fetch(url);
   const data = await response.json();
   setUsers(data);
 }, []);
 useEffect(() => {
   fetchUsers();
 }, [fetchUsers]);
 return (
   <div>
     {users.map((user) => (
       <p key={user.id}>
         {user.name}
       </p>
     ))}
   </div>
 );
}

Conclusion

The useEffect infinite loop is one of the most common React problems beginners face.

It mainly happens because:

  • State updates trigger re-renders
  • useEffect runs repeatedly
  • Dependency arrays are missing or incorrect

Mastering useEffect behavior helps developers create faster, cleaner, and more stable React applications.

Continue Learning React