logoESLint React

immutability

Validates against mutating props, state, and other values that are immutable.

This is an evaluation implementation and may contain false positives or negatives that have not yet been fully audited. Review each report carefully before applying fixes.

Full Name in eslint-plugin-react-x

react-x/immutability

Full Name in @eslint-react/eslint-plugin

@eslint-react/immutability

Features

🧪

Rule Details

A function that mutates a variable captured from an enclosing scope is, in effect, a mutable value: unlike a mutable array or object, the caller of the function has no way to prevent the mutation from happening once the function is invoked. This rule flags such functions when they are handed to a context that expects a stable, "frozen" value:

  • Passed as a JSX prop
  • Passed as an argument to a hook call
  • Returned from a custom hook

Mutations to ref-like values (an identifier named ref or ending in Ref) are exempted, since refs are mutable by design.

Examples

// 🔴 Problem: `fn` mutates `cache` after render
function Component() {
  const cache = new Map();
  const fn = () => {
    cache.set("key", "value");
  };
  return <Foo fn={fn} />;
}
// 🟢 Recommended: use state instead of a captured mutable variable
function Component() {
  const [cache, setCache] = useState(() => new Map());
  const fn = () => {
    setCache((prev) => new Map(prev).set("key", "value"));
  };
  return <Foo fn={fn} />;
}

Versions

Resources

Further Reading


See Also

  • react-x/no-direct-mutation-state
    Disallows direct mutation of this.state.
  • react-x/purity
    Validates that components and hooks are pure by checking that they do not call known-impure functions during render.
  • react-x/use-state
    Enforces correct usage of useState, including destructuring, symmetric naming of the value and setter, and wrapping expensive initializers in a lazy initializer function.

On this page