React Direct State Mutation

ID

javascript.react_direct_state_mutation

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

JavaScript

Tags

react, reliability, state

Description

Reports a write to this.state outside the constructor of a React class component: an assignment to one of its members, a replacement of the whole object, or an increment.

The constructor is exempt — assigning the initial this.state there is the documented way to create it. Only a class extending React’s component base (Component or PureComponent, however imported or qualified) is judged, because state is an ordinary member name and an ordinary class that keeps one is free to write it.

Rationale

// Bad
class Panel extends React.Component {
  constructor(props) {
    super(props);
    this.state = { open: false, seen: 0 };
  }

  open() {
    this.state.open = true;
    this.state.seen++;
  }
}
  • this.state is a value React owns. Writing it directly changes the object without telling React, so no render is scheduled and the screen keeps showing the previous value.

  • The change surfaces later, when some unrelated update renders the component — so the defect is intermittent by construction, and the line that caused it is nowhere near the render that shows it.

  • It also breaks the comparison React relies on: shouldComponentUpdate and componentDidUpdate are handed two references to the same mutated object and cannot see what changed.

Remediation

Go through setState, which records the new value and schedules the render. Build a new object or array rather than editing the one in state.

// Good
open() {
  this.setState({ open: true });
}

// Good — derived from the current value, so use the updater form
seen() {
  this.setState(previous => ({ seen: previous.seen + 1 }));
}

// Good — a new array, so the comparison sees a new value
add(row) {
  this.setState(previous => ({ rows: [...previous.rows, row] }));
}

© 2026 Xygeni. All rights reserved