Demo, all content is generated
Question

'Maximum update depth exceeded' after Cursor added a filter to my table

Solved · 301 views · asked by june_park · edited

Page freezes and the console shows

Error: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect...

This is what Cursor added:

const [filtered, setFiltered] = useState(rows);
const options = { status, search };

useEffect(() => {
  setFiltered(rows.filter((r) => matches(r, options)));
}, [rows, options]);
What I’ve tried

Asked Cursor to fix it, it removed options from the dependency array and added an eslint-disable comment. Now filtering doesn't update when I type.

Comment
Where does options come from? Is it created in the component? mei_lin · edited

3 answers

Marked as helpful by the asker
mei_lin · edited

options is a new object on every render, so the effect runs every render, calls setFiltered, which renders, which makes a new options... loop.

But the real fix is to not have that state at all. filtered is derived from rows, status and search, so just compute it:

const filtered = rows.filter((r) => matches(r, { status, search }));

If the list is big and it gets slow, wrap it in useMemo with [rows, status, search] (primitives, not an object). No effect, no extra state, no loop.

Rule of thumb: if a useEffect only calls a setState based on other state or props, it probably shouldn't exist.

Comment
Deleted 6 lines and it works better than before. Also removed the eslint-disable. june_park · edited
Also asked Cursor to add this rule to my rules file. It hasn't done it again since. june_park · edited
The 'derived state' rule is the single most useful React thing to put in your cursor rules. oksana_k · edited
pawel_z · edited

The React docs page 'You Might Not Need an Effect' covers this exact pattern and five similar ones. Worth giving to Cursor as context too.

Comment
oksana_k · edited

If you ever do need an object in deps, memoize the object with useMemo, or depend on its fields. But here Mei Lin's version is the right one.

Comment