Advertisement
programming React useEffect running endlessly or triggering twice? Learn why infinite loops happen in useEffect and how to fix them with real code examples and best practices.

Why does my React useEffect keep running in an infinite loop — and how do I fix it properly?

5 Min Read Verified Content

If you’ve worked with React long enough, you’ve probably run into this error:

Maximum update depth exceeded

Or you just notice your useEffect keeps firing over and over again — sometimes even calling APIs endlessly.

Let’s break down why this happens and how to fix it cleanly.


🔍 First: How useEffect Actually Works

Basic form:

useEffect(() => { // runs when dependencies change }, [dependencies]);

The dependency array controls when it runs.

Dependency ArrayEffect Runs
[]Once after mount
[value]Every time value changes
No arrayOn every render

Most infinite loops happen because we update state inside useEffect — which triggers a re-render — which triggers the effect again.


❌ Problem 1 — Updating State Inside useEffect With That State as Dependency

Example:

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

What happens?

  1. count = 0

  2. effect runs → setCount(1)

  3. count updates → triggers effect again

  4. setCount(2)

  5. repeat forever 🚨

✅ Fix: Remove State From Dependency or Refactor Logic

If you only want to run once:

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

Or better: update directly where needed — not in an effect unless necessary.


❌ Problem 2 — Fetching Data and Setting State Incorrectly

Beginner code often looks like this:

useEffect(() => { fetch("/api/users") .then(res => res.json()) .then(data => setUsers(data)); }, [users]);

Because users is in the dependency array, it updates every time users changes → infinite API calls.

✅ Fix: Remove users From Dependencies

useEffect(() => { fetch("/api/users") .then(res => res.json()) .then(data => setUsers(data)); }, []);

❌ Problem 3 — Objects/Arrays as Dependencies (They Change Every Render)

Example:

const filters = { sort: "asc" }; useEffect(() => { loadData(filters); }, [filters]);

Even if the values are same, a new object is created every render, so useEffect keeps firing.

✅ Fix: Memoize It

const filters = useMemo(() => ({ sort: "asc" }), []); useEffect(() => { loadData(filters); }, [filters]);

Now React sees the same object.


❌ Problem 4 — Functions as Dependencies

Same issue — functions are recreated every render.

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

✅ Fix: Wrap The Function in useCallback

const doSomething = useCallback(() => { // logic }, []); useEffect(() => { doSomething(); }, [doSomething]);

❌ Problem 5 — Missing Dependency (Looks Fixed But Actually Wrong)

Sometimes people do this:

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

This “fixes” the loop, but now the effect won’t update when value changes — which may be a bug.

Better approach: depend only on what matters and ensure no unnecessary state updates.


🧪 Common Real-World Pattern — API Call + Loading State

Here’s a safe pattern:

const [users, setUsers] = useState([]); const [loading, setLoading] = useState(false); useEffect(() => { setLoading(true); fetch("/api/users") .then(res => res.json()) .then(data => setUsers(data)) .finally(() => setLoading(false)); }, []);

✔ runs once
✔ no infinite loop
✔ clean UI handling


🔁 Why Does useEffect Run Twice in Dev Mode?

In React Strict Mode (development only), React intentionally calls effects twice to detect unsafe code.

This is normal in dev.

It does not happen in production.


🧠 Golden Rules To Avoid Loops

✔ Never update state that lives in the dependency list
✔ Memoize objects/functions used in dependencies
✔ Fetch data with [] unless filters change
✔ Understand that render → effect → render is normal
✔ Only put necessary values in the dependency array


🏁 Final Thoughts

useEffect isn’t broken.

It’s just honest — it runs whenever something it depends on changes.

Once you understand:

👉 “State change → re-render → effect runs based on dependencies”

…the bugs suddenly become predictable — and easy to fix.

Advertisement
Back to Programming