Sitemap

How to Fix “Too Many Re-Renders” in React (Infinite Loop in useEffect)

--

Why Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop from happening, and the 4 clean patterns fix every React developer needs.

Press enter or click to view image in full size
Design by Thamilkkumaran

You open your browser console during local development, hit refresh, and are instantly greeted by a brutal wall of red text:

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

Your CPU fan starts spinning, the UI freezes, and React forcibly shuts down execution.

This error is one of the most common friction points in React development, regardless of whether you are building with React 18 or React 19. The good news? React isn’t crashing randomly — it is actively saving your user’s browser from completely hanging.

Understanding why this loop occurs and how to fix it comes down to recognizing four recurring code patterns.

What Is React Doing Under the Hood?

React operates on a simple fundamental rule: State changes trigger component re-renders.

When you call a state updater like setCount(1), React schedules a render phase to recalculate the Virtual DOM. If during that render phase — or immediately after it executes — another state update is triggered before waiting for user interaction or an async event, React gets stuck in a loop:

$$\text{Render} \longrightarrow \text{State Update} \longrightarrow \text{Re-render} \longrightarrow \text{State Update} \longrightarrow \text{Infinite Loop}$$

To prevent your system memory from completely locking up, React places a hard limit on continuous recursive re-renders (typically around 50 consecutive renders) before throwing this explicit error.

Let’s break down the four exact code patterns that cause this and how to rewrite them correctly.

Pattern 1: Executing Event Handlers Immediately (The Classic Trap)

This is responsible for nearly 70% of all “Too Many Re-Renders” errors, especially when developers start passing arguments to handlers.

The Bug

Press enter or click to view image in full size
Design by Thamilkkumaran

Why it fails

Parentheses () mean invocation. When React evaluates the return block to render the JSX, it executes setItemsCount(itemsCount + 1) right away. This state update immediately queues another render, which evaluates the JSX again, triggering setItemsCount once more in an instant loop.

The Fix

Pass a function reference or wrap the handler in an inline anonymous arrow function so it only runs when the button is physically clicked.

Press enter or click to view image in full size
Design by Thamilkkumaran

Pattern 2: Direct State Updates inside Component Body

Updating state directly in the top-level execution path of a component function is a guaranteed way to break React’s render lifecycle.

The Bug

Press enter or click to view image in full size
Design by Thamilkkumaran

Why it fails

Every time UserStatus renders, it evaluates the if condition and immediately invokes setRole(‘admin’). Calling state updaters directly inside the body forces React to re-evaluate the component immediately, generating an infinite render cycle.

The Fix

If you need to derive state from existing props, do not use useState or useEffect at all. Calculate it directly during render (derived state):

Press enter or click to view image in full size
Design by Thamilkkumaran

If you must run a side effect based on prop changes, wrap the state update inside a controlled useEffect:

Press enter or click to view image in full size
Design by Thamilkkumaran

Pattern 3: The useEffect Dependency Loop

Get Selvarasa Thamilkkumaran’s stories in your inbox

Join Medium for free to get updates from this writer.

A frequent culprit in data-fetching components is modifying a state value inside useEffect while listing that same state value as a dependency.

The Bug

Press enter or click to view image in full size
Design by Thamilkkumaran

Why it fails

  1. The component mounts and runs useEffect.
  2. setUsers updates state, triggering a re-render.
  3. React checks the dependency array [users]. Since users updated, useEffect runs again.
  4. Steps 2 and 3 repeat infinitely.

The Fix

Remove the state variable from the dependency array if you only want the effect to run on component mount:

Press enter or click to view image in full size
Design by Thamilkkumaran

Pattern 4: Reference Instability with Non-Primitive Dependencies

JavaScript compares primitives (strings, numbers, booleans) by value, but arrays, objects, and functions are compared by reference.

The Bug

Press enter or click to view image in full size
Design by Thamilkkumaran

Why it fails

Even if userId doesn’t change, { id: userId, depth: 2 } creates a brand-new object in memory every time ProfileDashboard evaluates. React performs a shallow comparison on useEffect dependencies (Object.is(oldOptions, newOptions)), which returns false. useEffect fires, state updates, and the cycle repeats.

The Fix

Pass primitive properties directly into the dependency array, or wrap non-primitive objects using useMemo:

Press enter or click to view image in full size
Design by Thamilkkumaran

The 30-Second Debugging Checklist

When you see the “Too many re-renders” red screen in your application, check these four areas immediately:

  1. Check your JSX events: Are you writing onClick={handleClick()} instead of onClick={handleClick} or onClick={() => handleClick(id)}?
  2. Check your top-level component scope: Are you invoking any setState functions outside of an event handler or useEffect block?
  3. Check your useEffect dependencies: Are you setting a state variable inside an effect while keeping that same variable in the dependency array?
  4. Check for inline objects or functions: Are you creating objects or arrays inside component bodies and passing them directly into useEffect dependency arrays?

Fixing these subtle reference and execution errors will instantly eliminate infinite render cascades and keep your app performing smoothly.

--

--

Selvarasa Thamilkkumaran
Selvarasa Thamilkkumaran

Written by Selvarasa Thamilkkumaran

Web developer specializing in React.js, Next.js, TypeScript & Tailwind CSS. I build, debug, and improve modern web applications.