React State Setter Self Assign

ID

javascript.react_state_setter_self_assign

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

JavaScript

Tags

react, reliability, state

Description

Reports a useState setter called with the very state variable it writes, setCount(count), which stores the value the slot already holds.

The pairing comes from the useState destructuring, so only the setter of that exact slot counts: setLeft(right) is an ordinary update between two slots and is not reported. Only a bare variable argument is judged — setCount(count + 1) and setCount(previous ⇒ previous) are different expressions and this rule does not evaluate them.

Rationale

import { useState } from "react";

// Bad — the handler appears not to fire
function Counter() {
  const [count, setCount] = useState(0);
  const keep = () => setCount(count);
  return <button onClick={keep}>{count}</button>;
}

// Bad — the array was mutated in place, so React sees the same value
function Table() {
  const [rows, setRows] = useState([]);
  const sort = () => {
    rows.sort(byName);
    setRows(rows);
  };
  return <List rows={rows} onSort={sort} />;
}
  • React compares the new state with the current one and bails out when they are the same value. The call renders nothing and changes nothing, and nothing warns, because passing the current value is legal.

  • The second form is the costly one: the state really did change, the screen does not, and the code that changed it looks correct.

Remediation

Pass the new value. When it derives from the current one, use the updater form, which also removes the stale-closure hazard. When the state is an array or an object, produce a new one rather than mutating the old.

import { useState } from "react";

// Good
function Counter() {
  const [count, setCount] = useState(0);
  const bump = () => setCount(count + 1);
  return <button onClick={bump}>{count}</button>;
}

// Good — updater form, correct even in a stale closure
function StaleSafeCounter() {
  const [count, setCount] = useState(0);
  const bump = () => setCount(previous => previous + 1);
  return <button onClick={bump}>{count}</button>;
}

// Good — a new array, so React sees a new value
function Table() {
  const [rows, setRows] = useState([]);
  const sort = () => setRows([...rows].sort(byName));
  return <List rows={rows} onSort={sort} />;
}

© 2026 Xygeni. All rights reserved