πŸ”§ Error Fixes

React: Too Many Re-renders β€” How to Fix Infinite Loops


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

Your component is updating state during render, which triggers another render, which updates state again β€” infinite loop. React catches this and stops it.

Cause 1: Calling a Function Instead of Passing a Reference

The most common cause.

// ❌ This CALLS handleClick on every render
<button onClick={handleClick()}>Click me</button>

// βœ… This PASSES handleClick as a reference
<button onClick={handleClick}>Click me</button>

// βœ… If you need to pass arguments, wrap in arrow function
<button onClick={() => handleClick(id)}>Click me</button>

The difference: handleClick() runs immediately. handleClick or () => handleClick(id) runs only when clicked.

Cause 2: setState During Render

// ❌ Setting state directly in the component body
function Counter() {
  const [count, setCount] = useState(0);
  setCount(count + 1); // πŸ’₯ runs every render!
  return <div>{count}</div>;
}

// βœ… Set state in an event handler or useEffect
function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}

Cause 3: useEffect Without Dependency Array

// ❌ Runs after EVERY render, sets state, triggers re-render β†’ loop
useEffect(() => {
  setData(fetchData());
});

// βœ… Empty array = run once on mount
useEffect(() => {
  fetchData().then(setData);
}, []);

// βœ… With dependencies = run when dependencies change
useEffect(() => {
  fetchData(userId).then(setData);
}, [userId]);

Cause 4: Object/Array in Dependency Array

// ❌ New object every render β†’ useEffect runs every render β†’ loop
function MyComponent({ filters }) {
  const options = { ...filters, limit: 10 }; // new object each render

  useEffect(() => {
    fetchData(options).then(setData);
  }, [options]); // πŸ’₯ options is always "new"
}

// βœ… Memoize the object
function MyComponent({ filters }) {
  const options = useMemo(
    () => ({ ...filters, limit: 10 }),
    [filters]
  );

  useEffect(() => {
    fetchData(options).then(setData);
  }, [options]);
}

Cause 5: Conditional setState That Always Triggers

// ❌ If items always has length > 0, this loops forever
useEffect(() => {
  if (items.length > 0) {
    setHasItems(true); // triggers re-render β†’ useEffect runs again
  }
}, [items]);

// βœ… Only set state if it actually changed
useEffect(() => {
  const newValue = items.length > 0;
  setHasItems(prev => prev === newValue ? prev : newValue);
}, [items]);

// βœ… Or better: derive it instead of storing in state
const hasItems = items.length > 0; // no state needed!

Debugging Tips

  1. Add console.log('render') at the top of your component β€” if it prints endlessly, you have the loop
  2. Check every onClick β€” are you calling fn() instead of passing fn?
  3. Check every useEffect β€” does it have a dependency array?
  4. Check every setState call β€” is it inside a handler/effect, or in the render body?
  5. Use React DevTools Profiler to see what’s causing re-renders