React State Update During Render

ID

javascript.react_set_state_in_render

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

JavaScript

Tags

react, reliability, state

Description

Reports a React state update performed while the component is rendering: a useState setter called in the body of the function component, or this.setState called in a class component’s render().

Only an unconditional update is reported. Setting state during render behind a guard is a supported pattern for state derived from props, and it terminates because the guard stops being true. An update inside an event handler, an effect callback or any other nested function is not a render-phase update and is left alone.

Rationale

import React, { useState } from "react";

// Bad — the component never settles
function Counter() {
  const [count, setCount] = useState(0);
  setCount(count + 1);
  return <span>{count}</span>;
}

// Bad — the class form of the same loop
class Panel extends React.Component {
  render() {
    this.setState({ open: true });
    return <div />;
  }
}
  • A state update asks React to render the component again. Performed while that render is still running, the new render runs the same line, which asks for another one.

  • React stops the function-component form with "Too many re-renders"; the class form spins until the tab stops responding. Neither is a slow path that can be tuned — the component simply does not work.

  • The mistake is easy to make while moving a line out of a handler, because the line itself is unchanged and only its position is wrong.

Remediation

Move the update to where React expects it: an event handler, an effect, or a lifecycle method. When the value is really derived from props, compute it during render instead of storing it, or guard the update so it stops.

import { useState } from "react";

// Good — the update belongs to an event
function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

// Good — derived value, no state at all
function Total({ items }) {
  const total = items.reduce((sum, item) => sum + item.price, 0);
  return <span>{total}</span>;
}

// Good — guarded derivation, which terminates
function Derived({ value }) {
  const [previous, setPrevious] = useState(value);
  if (value !== previous) {
    setPrevious(value);
  }
  return <span>{previous}</span>;
}

© 2026 Xygeni. All rights reserved