facebook/react · error · Error

Too many re-renders. React limits the number of renders to p

Error message

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

What it means

dispatchAction in Fizz throws when a state setter is called during render more than RE_RENDER_LIMIT (25) times. Each render-phase update makes React re-run the component immediately; an unconditional or insufficiently guarded setter call loops forever, and the limit stops the infinite loop.

Source

Thrown at packages/react-server/src/ReactFizzHooks.js:565

    const ref = {current: initialValue};
    if (__DEV__) {
      Object.seal(ref);
    }
    // $FlowFixMe[incompatible-use] found when upgrading Flow
    workInProgressHook.memoizedState = ref;
    return ref;
  } else {
    return previousRef;
  }
}

function dispatchAction<A>(
  componentIdentity: Object,
  queue: UpdateQueue<A>,
  action: A,
): void {
  if (numberOfReRenders >= RE_RENDER_LIMIT) {
    throw new Error(
      'Too many re-renders. React limits the number of renders to prevent ' +
        'an infinite loop.',
    );
  }

  if (componentIdentity === currentlyRenderingComponent) {
    // This is a render phase update. Stash it in a lazily-created map of
    // queue -> linked list of updates. After this render pass, we'll restart
    // and apply the stashed updates on top of the work-in-progress hook.
    didScheduleRenderPhaseUpdate = true;
    const update: Update<A> = {
      action,
      next: null,
    };
    if (renderPhaseUpdates === null) {
      renderPhaseUpdates = new Map();
    }
    const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);

View on GitHub (pinned to eafeac097b)

Solutions

  1. Guard render-phase updates: only call the setter when the value actually changed (if (state.source !== prevProp) setState(...)) so React settles after one re-render
  2. Derive the value during render instead of storing it — remove the state entirely
  3. If the update must happen later, move it to a client component event handler or effect

Example fix

// before
function Counter() {
  const [n, setN] = useState(0);
  setN(n + 1); // setter every render -> 25 re-renders -> throws
  return <p>{n}</p>;
}

// after
function Counter() {
  const [n, setN] = useState(0);
  return <button onClick={() => setN(n + 1)}>{n}</button>;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// The supported render-phase update pattern: only setState when the value changed.
const [state, setState] = useState({source: props.value, data: null});
if (state.source !== props.value) {
  setState(s => ({...s, source: props.value})); // settles after one re-render
}

Try / catch

const {pipe} = renderToPipeableStream(<App/>, {
  onError(err) {
    if (String(err.message).includes('Too many re-renders')) {
      // An unguarded setter runs during render — log the component stack and fail the render.
      logError(err);
    }
  },
});

Prevention

When it happens

Trigger: const [n, setN] = useState(0); setN(n + 1) in the body of a server-rendered component; setters invoked in render helpers without the change-guard the render-phase-update pattern requires; the same setter firing on every render pass so the count climbs to 25.

Common situations: Derived-state patterns ('sync state to props') written as an immediate setState during render; one-time initialization implemented as setState at render; client code with effect-based updates migrated into SSR'd components.

Related errors


AI-assisted analysis of facebook/react@eafeac097b (2026-08-21). Data as JSON: /api/errors/80892f8c5e844a3e. Report an issue: GitHub.