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.
optionscome from? Is it created in the component? mei_lin · edited