How to Fix 'Too Many Re-renders' in React
By Pawan Singh · Jun 2026
"Too many re-renders. React limits the number of renders to prevent an infinite loop" is React's own circuit breaker catching a bug before it freezes the browser tab entirely. The error is genuinely helpful once you know what it's telling you.
The Most Common Cause: Calling the Setter During Render
function Counter() {
const [count, setCount] = useState(0);
setCount(count + 1); // Bug: runs on every render, unconditionally
return <div>{count}</div>;
}
This calls setCount directly in the component body, not inside an event handler or effect. Every render calls the setter, which schedules another render, which calls the setter again — an infinite loop React's built-in render limit exists specifically to catch and stop.
Cause 2: An Inline Function Passed as an onClick Handler, Called Instead of Referenced
// Bug: calls setCount immediately during render, not on click
<button onClick={setCount(count + 1)}>Increment</button>
// Fixed: passes a function reference, only called on actual click
<button onClick={() => setCount(count + 1)}>Increment</button>
The parentheses are the entire bug here — onClick={setCount(count + 1)} invokes setCount immediately while React is rendering the JSX, not when the button is actually clicked.
Cause 3: A useEffect With a Missing or Wrong Dependency Array
// Bug: no dependency array at all means this effect runs after every render,
// and it updates state, which triggers another render, forever
useEffect(() => {
setCount(count + 1);
});
// Fixed: dependency array present, and doesn't include something the
// effect itself changes on every run
useEffect(() => {
fetchInitialCount().then(setCount);
}, []); // runs once, on mount
An effect that updates a piece of state, and also lists that same state in its own dependency array (or has no dependency array at all, which means "run after every render"), is the second most common source of this error — the effect keeps re-triggering itself.
Cause 4: Derived State Set During Render Instead of Computed Directly
// Bug: syncing one piece of state from another, in the render body
function UserCard({ user }) {
const [displayName, setDisplayName] = useState('');
setDisplayName(user.name.toUpperCase()); // runs every render, loops
return <div>{displayName}</div>;
}
// Fixed: just compute it, no state or effect needed at all
function UserCard({ user }) {
const displayName = user.name.toUpperCase();
return <div>{displayName}</div>;
}
This case is worth calling out specifically because the fix usually isn't "wrap it in useEffect" — it's realizing the value never needed to be state in the first place. Anything fully derivable from props or existing state on every render should just be a plain calculation, not synced state.
How to Actually Debug It
- Read the component name in the error/stack trace — React tells you which component is looping, so start there rather than guessing across the whole app.
- Search that component for every state setter call and check where each one is: inside JSX/render body (bug), inside an event handler (fine), or inside
useEffect(check the dependency array carefully). - For any
onClick={...}(or similar prop), check for missing arrow-function parentheses —onClick={fn()}vsonClick={fn}vsonClick={() => fn()}are three different things with very different timing. - For any
useEffectthat calls a setter, ask whether the value it's setting is truly independent state, or just a derived calculation that shouldn't be state at all.
React's Strict Mode Can Make This More Visible, Not Cause It
In development, <React.StrictMode> intentionally double-invokes some functions to help surface exactly this class of bug earlier — if a loop only appears in Strict Mode, it's still a real bug, not a Strict Mode artifact; production would eventually hit the same loop under the right conditions.
Chasing down a stubborn React rendering bug? Get in touch — I work as a freelance React developer.