Diagnosing and Fixing React's 'Too Many Re-renders' Error
A diagnostic guide for React's 'Too many re-renders' error: recognize the infinite-loop condition, use a cause table, run ordered checks, apply targeted fixes, and verify with React DevTools Profiler.
15 Jun 2026, 13:16 UTC

Recognizable Condition
React throws Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. This appears in the browser console when a component triggers state updates during its own render phase, causing React to re-render repeatedly until the internal limit is reached. The component stack trace usually points to the offending component, but the root cause can be a prop change, an effect, or an event handler that fires immediately.
Cause and Diagnostic Table
| Symptom | Likely Cause | Quick Diagnostic |
|---|---|---|
| Error on initial mount | State setter called directly in component body | Search for setState(...) or dispatch(...) outside any callback or useEffect |
| Error after a user click | Event handler invokes setter immediately: onClick={setCount(c+1)} | Inspect JSX for missing arrow function wrapper |
| Error after prop change | useEffect updates state without proper dependency array | Check effect dependencies; missing or new object/array references each render |
| Error in a loop of components | Parent passes new function/object props each render, triggering child effects | Wrap callbacks in useCallback and memoize objects with useMemo |
Ordered Checks
- Locate the component from the error stack. Open the file and look for any state setter (
setX,dispatch) executed at the top level of the function body. - Check event handlers in JSX. A common mistake is
onClick={setCount(count + 1)}instead ofonClick={() => setCount(count + 1)}. The former calls the setter during render. - Inspect
useEffecthooks in the same component. An effect that calls a setter must have a dependency array that does not change on every render. Empty array[]runs once; specific values[propId]run only when those values change. - Verify prop stability. If the component receives callbacks or objects from a parent, ensure the parent memoizes them with
useCallbackoruseMemo. New references each render will re-trigger effects. - Run the React DevTools Profiler (see verification step) to confirm the render count spikes and identify the exact update source.
Fixes Tied to Findings
1. State Setter in Component Body
Problem:
function Counter() {
const [count, setCount] = useState(0);
setCount(count + 1); // runs every render
return <div>{count}</div>;
}Fix: Move the update into an effect with a dependency that reflects the intended trigger, or remove it if the value can be derived during render.
function Counter() {
const [count, setCount] = useState(0);
// If you need to increment once on mount:
useEffect(() => {
setCount(c => c + 1);
}, []);
return <div>{count}</div>;
}2. Immediate Invocation in Event Handler
Problem:
<button onClick={setCount(count + 1)}>Increment</button>Fix: Wrap in an arrow function so the setter runs only on click.
<button onClick={() => setCount(count + 1)}>Increment</button>3. Effect Missing Dependencies or Unstable References
Problem:
useEffect(() => {
setData(fetchData(props.id)); // props.id changes, but effect runs every render if deps missing
});Fix: Add the exact dependencies. If fetchData is a function from props, ensure the parent wraps it in useCallback.
useEffect(() => {
setData(fetchData(props.id));
}, [props.id, fetchData]); // fetchData must be stable4. Derived State Instead of Synchronized State
When a value can be computed from props or existing state during render, avoid a separate state variable and an effect to keep them in sync. This eliminates a whole class of render loops.
// Instead of:
const [fullName, setFullName] = useState('');
useEffect(() => setFullName(`${first} ${last}`), [first, last]);
// Derive during render:
const fullName = `${first} ${last}`;Verification Steps
- Reproduce the error in development mode (the error only appears in development).
- Apply the relevant fix from above.
- Open React DevTools → Profiler. Record a session interacting with the component. The flame graph should show a single render per interaction, not a continuous cascade.
- Check the console: the "Too many re-renders" error should no longer appear.
Limitations and Escalation
- If the error persists after applying the above fixes, the loop may originate in a third‑party library or a custom hook that internally updates state. Inspect the hook's implementation or file an issue with the library.
- Complex state machines or concurrent features (e.g.,
useTransition) can mask the loop in development but surface in production. Test withreact@experimentalbuilds if you suspect concurrent rendering interactions. - When the component tree is large, use the Profiler's "Record why each component rendered" setting to pinpoint the exact prop or state change causing the cascade.
Practical Checklist
- No state setter called directly in component body.
- All event handlers use arrow functions or bound methods.
- Every
useEffectthat updates state has a complete, stable dependency array. - Parent components memoize callbacks and object props passed to children.
- Derived values are computed during render, not stored in separate state.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.