How to Debug an Infinite useEffect Loop

An effect is a synchronization rule—and the loop is usually the rule explaining itself.

By · · 5 min read · React · Debugging

An infinite loop needs two ingredients: the effect updates something, and that update changes one of its dependencies. Find both sides before changing the dependency array.

Draw the cycle

  1. Write down every dependency.
  2. Identify which one changes after the effect runs.
  3. Find the setter, dispatch, or parent update that changes it.
  4. Decide whether the effect is synchronizing an external system.
// Cycles because setOptions creates a new object
useEffect(() => {
  setOptions({ mode: "dark" });
}, [options]);

Watch identity

Object literals and inline functions created during render are new on every render, even when their contents look identical.

Better boundary

If the effect only derives data from props or state, calculate it during render or with useMemo. Reserve effects for external synchronization.

Use a stable dependency

Move stable configuration outside the component, memoize a real boundary, or depend on the primitive fields the effect actually needs. Do not disable the lint rule to hide the cycle.

Still stuck with a React or Next.js bug? See React & Next.js debugging help.

Related notes

React State Not Updating? What to Check
React Component Not Re-rendering? Common Causes