react-hooks/immutability
Disallow modifying props, state, and other values that React expects to stay unchanged
Also called react_hooks/immutability, react/immutability
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#
example.jsx1 problem
function Price({ product }) {
productError: This value cannot be modifiedModifying component props or hook arguments is not allowed. Consider using a local variable instead..price = product.price / 2;
return <span>{product.price}</span>;
}example.jsx1 problem
import { useState } from "react";
function Profile() {
const [user, setUser] = useState({ name: "" });
const rename = name => {
userError: This value cannot be modifiedModifying a value returned from useState(), which should not be modified directly. Use the setter function to update instead..name = name;
setUser(user);
};
return <button onClick={() => rename("Ada")}>{user.name}</button>;
}example.jsx1 problem
function Box() {
const style = { color: "red" };
const box = <div style={style} />;
styleError: This value cannot be modifiedModifying a value used previously in JSX is not allowed. Consider moving the modification before the JSX..color = "blue";
return box;
}example.jsx2 problems
import { useEffect } from "react";
const last = { title: "" };
function useTitle(options) {
delete optionsError: This value cannot be modifiedModifying component props or hook arguments is not allowed. Consider using a local variable instead..icon;
lastError: This value cannot be modifiedModifying a variable defined outside a component or hook is not allowed. Consider using an effect..title = options.title;
useEffect(() => {
document.title = options.title;
});
}example.jsx2 problems
function Counter() {
let count = 0;
const onClick = () => {
countError: Cannot reassign variable after render completesReassigning count after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. = count + 1;
};
return <button onClick={onClickError: Cannot modify local variables after render completesThis argument is a function which may reassign or mutate count after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.}>{count}</button>;
}example.jsxNo problems
function Price({ product }) {
const discounted = { ...product, price: product.price / 2 };
discounted.label = "Sale";
return <span>{discounted.label}: {discounted.price}</span>;
}example.jsxNo problems
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:
example.jsxNo problems
function Tags({ tags }) {
tags.push("new");
return <span>{tags.length}</span>;
}