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:
[lint.rules]
"react-hooks/set-state-in-render" = "error" # or "warn"Examples#
import { useState } from "react";
function Cart({ items }) {
const [count, setCount] = useState(0);
setCountError: Cannot call setState during renderCalling setState during render may trigger an infinite loop.To reset state when other state/props change, store the previous value in state and update conditionally: react.dev/reference/react/useState#storing-information-from-previous-rendersTo derive data from other state/props, compute the derived data during render without using state.(items.length);
return <span>{count}</span>;
}A useMemo() callback runs during render too:
import { useMemo, useState } from "react";
function Cart({ items }) {
const [count, setCount] = useState(0);
const total = useMemo(() => {
setCountError: Calling setState from useMemo may trigger an infinite loopEach time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (react.dev/reference/react/useState).(items.length);
return items.reduce((sum, item) => sum + item.price, 0);
}, [items]);
return <span>{count} items, {total}</span>;
}function Cart({ items }) {
const count = items.length;
return <span>{count}</span>;
}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:
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>;
}