The useEffect that fires forever: an object in the dependency array is a new reference every render
verbatim errorWarning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
Problem
A dashboard component re-fetched on every keystroke and eventually React gave up:
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.Network tab showed the same API call looping dozens of times per second.
Root cause
The dependency array contained a freshly created object:
function Dashboard({ filters }) {
const [data, setData] = useState(null);
useEffect(() => {
fetch(`/api/stats?${new URLSearchParams(filters)}`)
.then((r) => r.json())
.then(setData);
}, [{ ...filters }]); // <-- new object identity on EVERY render
return <Chart data={data} filters={filters} />;
}useEffect compares deps with Object.is — reference equality. { ...filters } === { ...filters } is false, so the effect "changed" after every render; setData re-renders; the effect re-runs; forever.
fix preview — first 3 of 6 lines (tsx), truncated:
const { from, to, segment } = filters;
useEffect(() => {
fetch(`/api/stats?from=${from}&to=${to}&segment=${segment}`)
… 3 more lines in the fix🔒 the fix — including 4 code blocks — is members-only. $1/mo unlocks everything.