react-hooks/set-state-in-render

Disallow calling a state setter unconditionally while a component renders

Also called react_hooks/set-state-in-render, react/set-state-in-render

Setting state makes React render the component again. If the component sets state every time it renders, it never stops, and React gives up with "Too many re-renders".

The rule reports a setter that's always called during render, directly or through a function the component calls. It also reports every setter called in a useMemo() callback. It doesn't report a setter behind a condition, or one in an event handler or an effect.

Configure#

Off by default. To turn it on:

bunfig.toml
[lint.rules]
"react-hooks/set-state-in-render" = "error" # or "warn"

Examples#

example.jsx1 problem
import { useState } from "react";

function Cart({ items }) {
  const [count, setCount] = useState(0);
  setCount(items.length);
  return <span>{count}</span>;
}

A useMemo() callback runs during render too:

example.jsx1 problem
import { useMemo, useState } from "react";

function Cart({ items }) {
  const [count, setCount] = useState(0);
  const total = useMemo(() => {
    setCount(items.length);
    return items.reduce((sum, item) => sum + item.price, 0);
  }, [items]);
  return <span>{count} items, {total}</span>;
}
example.jsxNo problems
function Cart({ items }) {
  const count = items.length;
  return <span>{count}</span>;
}
example.jsxNo problems
import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

A setter behind a condition is how you reset state when a prop changes. The condition is false on the next render, so the loop ends:

example.jsxNo problems
import { useState } from "react";

function List({ items }) {
  const [previous, setPrevious] = useState(items);
  const [selected, setSelected] = useState(null);
  if (items !== previous) {
    setPrevious(items);
    setSelected(null);
  }
  return <ul onClick={() => setSelected(0)}>{selected}</ul>;
}