Fix "Too many re-renders" Error in React Component

intermediateโš›๏ธ React2026-03-19| React 16.8+, any OS, any browser โ€” occurs in both development and production builds

Error Message

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

The Error

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

React bails out after hitting a render limit โ€” roughly 25 cycles in development. Your app freezes, the console floods with the same error, and sometimes the browser tab crashes entirely. It almost always means a component is scheduling its own re-render on every paint.

Root Cause

Three patterns cause almost every case:

  • Calling a state setter directly in the render body โ€” not inside a handler or effect
  • Passing an invoked function to an event prop instead of a function reference
  • A useEffect whose dependency changes on every render, creating a loop

Fix 1: State setter called during render

The most common culprit. The setter fires, triggers a re-render, the setter fires again. React kills it at ~25 iterations before the browser dies.

// โŒ Wrong โ€” setCount runs on every render
function Counter() {
  const [count, setCount] = useState(0);
  setCount(count + 1); // direct call in render body
  return <div>{count}</div>;
}

// โœ… Fix โ€” move it into useEffect or an event handler
function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setCount(count + 1); // runs after mount, not during render
  }, []); // empty deps = once only

  return <div>{count}</div>;
}

Fix 2: Calling the function instead of passing it

One extra pair of parentheses causes this. onClick={handleClick()} calls the function immediately during render. onClick={handleClick} passes it as a reference for React to call later, on click.

// โŒ Wrong โ€” handleClick() executes during render, not on click
function MyButton() {
  const [open, setOpen] = useState(false);

  function handleClick() {
    setOpen(true);
  }

  return <button onClick={handleClick()}>Open</button>;
}

// โœ… Fix โ€” pass the reference, no parentheses
return <button onClick={handleClick}>Open</button>;

Need to pass arguments? Wrap it in an arrow function instead:

// โœ… Arrow function โ€” safe, does not invoke on render
return <button onClick={() => handleClick(someId)}>Open</button>;

Fix 3: useEffect dependency loop

An effect that modifies one of its own dependencies runs forever. State changes โ†’ effect re-runs โ†’ state changes again. Breaking the cycle means removing the state variable from the dependency array.

// โŒ Wrong โ€” items changes โ†’ effect runs โ†’ items changes โ†’ loop
useEffect(() => {
  setItems([...items, newItem]);
}, [items]);

// โœ… Fix โ€” functional updater reads latest state without needing it in deps
useEffect(() => {
  setItems(prev => [...prev, newItem]);
}, [newItem]); // only re-runs when newItem changes

Fix 4: Inline object or array as a dependency

This one is subtle. JavaScript creates a brand-new object reference on every render, even when the values inside are identical. React uses reference equality for dependency checks โ€” so { id: 1 } written inline is always "changed".

// โŒ Wrong โ€” { id: 1 } is a new reference on every render
useEffect(() => {
  fetchData({ id: 1 });
}, [{ id: 1 }]);

// โœ… Fix โ€” stabilize with useMemo so the reference stays the same between renders
const params = useMemo(() => ({ id: 1 }), []);

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

Fix 5: Conditional setState that never stops

Setting state inside an if block looks harmless โ€” but when the condition is always true, it fires on every single render.

// โŒ Wrong โ€” condition is always true while data exists
function Form({ data }) {
  const [value, setValue] = useState('');

  if (data) {
    setValue(data.name); // runs every render as long as data is truthy
  }

  return <input value={value} />;
}

// โœ… Fix โ€” move the sync into useEffect with data as the dependency
function Form({ data }) {
  const [value, setValue] = useState('');

  useEffect(() => {
    if (data) {
      setValue(data.name);
    }
  }, [data]); // only runs when data actually changes

  return <input value={value} />;
}

How to Find Which Component Is Looping

React's stack trace often points at internal fiber code, not your file. Use these steps to pinpoint the actual offender:

  • Open React DevTools โ†’ Profiler tab โ†’ hit Record โ†’ your looping component will show 50โ€“200 renders in under a second
  • Drop console.count('MyComponent') at the top of each suspect โ€” the one printing 100+ is your culprit
  • Comment out useEffect hooks one by one until the loop stops โ€” that's the one causing it
  • Scan every event prop (onClick, onChange, onSubmit) for accidental () after the handler name

Verify the Fix

  • Error disappears from the console
  • React DevTools Profiler shows the component rendering only on user interaction or parent re-render โ€” not on its own
  • console.count stops climbing after the initial mount
  • Network tab confirms API calls fire once per action, not in a continuous stream

Prevention Checklist

  • State setters belong in event handlers or effects โ€” never in the render body
  • Event props take references: onClick={fn}, not calls: onClick={fn()}
  • Inside useEffect, prefer the functional updater form (prev => ...) and drop the state variable from the dependency array
  • Wrap objects and arrays used as effect dependencies in useMemo or useCallback to stabilize their references
  • Add eslint-plugin-react-hooks to your project โ€” the exhaustive-deps rule catches most of these before they ever reach the browser

Related Error Notes