Skip to content
NewBun 1.4.5 is out. Release notes
AphrodyBun

Linter · Rules

react-hooks/immutability

Disallow modifying props, state, and other values that React expects to stay unchanged

React compares values by identity to decide what to render again. If you change an object in place, it's still the same object, so React sees no change and the screen keeps showing the old data. Changing a prop also changes the parent's copy.

The rule reports assigning to a property, or using delete on one, when the property belongs to:

  • props and the arguments of a hook
  • a value returned by useState, useContext, and other hooks
  • a value that was already passed to a hook or used in JSX
  • a variable declared outside the component

It also reports a local variable that's reassigned after render, for example in an event handler.

Configure

Off by default. To turn it on:

bunfig.toml
[lint.rules]
"react-hooks/immutability" = "error" # or "warn"

Examples

Reported:

function Price({ product }) {
  product.price = product.price / 2;
  return <span>{product.price}</span>;
}
import { useState } from "react";

function Profile() {
  const [user, setUser] = useState({ name: "" });
  const rename = name => {
    user.name = name;
    setUser(user);
  };
  return <button onClick={() => rename("Ada")}>{user.name}</button>;
}
function Box() {
  const style = { color: "red" };
  const box = <div style={style} />;
  style.color = "blue";
  return box;
}
import { useEffect } from "react";

const last = { title: "" };

function useTitle(options) {
  delete options.icon;
  last.title = options.title;
  useEffect(() => {
    document.title = options.title;
  });
}
function Counter() {
  let count = 0;
  const onClick = () => {
    count = count + 1;
  };
  return <button onClick={onClick}>{count}</button>;
}

Not reported:

function Price({ product }) {
  const discounted = { ...product, price: product.price / 2 };
  discounted.label = "Sale";
  return <span>{discounted.label}: {discounted.price}</span>;
}
import { useState } from "react";

function Profile() {
  const [user, setUser] = useState({ name: "" });
  const rename = name => {
    setUser({ ...user, name });
  };
  return <button onClick={() => rename("Ada")}>{user.name}</button>;
}

The rule doesn't know what a method call does. This modifies the parent's array but isn't reported:

function Tags({ tags }) {
  tags.push("new");
  return <span>{tags.length}</span>;
}