Try @eslint-react/kit@beta
logoESLint React

no-direct-mutation-state

Disallows direct mutation of 'this.state'.

Full Name in eslint-plugin-react-x

react-x/no-direct-mutation-state

Full Name in @eslint-react/eslint-plugin

@eslint-react/no-direct-mutation-state

Presets

x recommended recommended-typescript recommended-type-checked strict strict-typescript strict-type-checked

Rule Details

Never mutate this.state directly, as calling setState() afterward may replace the mutation you made. Treat this.state as if it were immutable.

The only place it's acceptable to assign this.state is in a class component's constructor.

Examples

Mutating this.state directly in an event handler

Mutating this.state directly does not trigger a re-render, and a subsequent setState may overwrite the change.

import React from "react";

interface MyComponentProps {}

interface MyComponentState {
  foo: string;
}

// Problem: Assigning to this.state directly
class MyComponent extends React.Component<MyComponentProps, MyComponentState> {
  state = {
    foo: "bar",
  };

  render() {
    return (
      <div
        onClick={() => {
          this.state.foo = "baz";
          //   ^^^ Do not mutate state directly. Use 'setState()' instead.
        }}
      >
        {this.state.foo}
      </div>
    );
  }
}
import React from "react";

interface MyComponentProps {}

interface MyComponentState {
  foo: string;
}

// Recommended: Update state via setState
class MyComponent extends React.Component<MyComponentProps, MyComponentState> {
  state = {
    foo: "bar",
  };

  render() {
    return (
      <div
        onClick={() => {
          this.setState({ foo: "baz" });
        }}
      >
        {this.state.foo}
      </div>
    );
  }
}

Mutating state in function components

Directly mutating a state object or array in a function component does not trigger a re-render and breaks React's state management.

// Problem: Mutating a state object directly instead of using the setter
import { useState } from "react";

function UserProfile() {
  const [user, setUser] = useState({ name: "Taylor", age: 25 });

  function handleClick() {
    user.age = user.age + 1; // Mutates state directly — won't re-render!
  }

  return (
    <button onClick={handleClick}>
      {user.name} is {user.age} years old
    </button>
  );
}
// Recommended: Always use the setter function with a new object
import { useState } from "react";

function UserProfile() {
  const [user, setUser] = useState({ name: "Taylor", age: 25 });

  function handleClick() {
    setUser({ ...user, age: user.age + 1 });
  }

  return (
    <button onClick={handleClick}>
      {user.name} is {user.age} years old
    </button>
  );
}

Versions

Resources

Further Reading


See Also

On this page