How to Fix a React useEffect Search Filter That Won't Update

A React search input that does nothing as you type almost always has an empty useEffect dependency array - [] tells React to run the effect once on mount and never again, so the filter computes against the initial empty string and stays frozen. Add the query variable to the array and the list recomputes on every keystroke.

Fullstack EngineerfullstackreactuseEffect

Why the filter never runs again

Here is the typical pattern:

const [query, setQuery] = useState('');
const [filtered, setFiltered] = useState<string[]>(ITEMS);

useEffect(() => {
  setFiltered(ITEMS.filter(i => i.toLowerCase().includes(query.toLowerCase())));
}, []); // runs once - query is '' on mount and never re-read

On mount query is '', so the effect runs, sees all items match, and sets filtered to the full list. After that, [] tells React this effect has no reactive dependencies - no value it watches can change - so it never re-runs. Typing updates query in state, which triggers a re-render, but the effect (and therefore filtered) never moves.

The fix: add the dependency

Include every piece of state or prop the effect reads inside the array:

useEffect(() => {
  setFiltered(ITEMS.filter(i => i.toLowerCase().includes(query.toLowerCase())));
}, [query]); // re-runs every time query changes

Now React sees that query is a dependency. When setQuery is called (on every keystroke), the state updates, the component re-renders, and React runs the effect again with the new query value - so filtered always reflects what the user typed.

Catch it at PR time with ESLint

The react-hooks/exhaustive-deps rule flags missing dependencies automatically:

{
  "rules": {
    "react-hooks/exhaustive-deps": "warn"
  }
}

A warning in the editor is the fastest way to catch this before it ships.

The better pattern: no effect needed

A filtered list derived from state doesn't need useEffect at all - compute it during render:

const [query, setQuery] = useState('');
const filtered = ITEMS.filter(i => i.toLowerCase().includes(query.toLowerCase()));

Every re-render recomputes filtered from the current query. No effect, no dependency array, no stale-closure risk. useEffect is for syncing with things outside React - network calls, DOM imperative APIs, subscriptions. Derived state from existing state is not a side effect; it's just a variable.

If the filter is expensive, wrap it in useMemo to recompute only when query or the list changes - still no effect:

const filtered = useMemo(
  () => ITEMS.filter(i => i.toLowerCase().includes(query.toLowerCase())),
  [query]
);

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

Why does my React search filter not work when I type?

The most common cause is an empty useEffect dependency array - []. The effect runs once on mount (when the query is still empty) and never again, so the filtered list never updates. Add the query state variable to the dependency array.

What does the useEffect dependency array do?

It tells React which values the effect depends on. React re-runs the effect after a render only if at least one dependency changed. An empty [] means no dependencies - run once on mount, never again. Omitting a value the effect reads causes stale data bugs.

Should I use useEffect to filter a list in React?

Usually no. If the filtered list is derived from state you already have, compute it during render (const filtered = items.filter(...)) or with useMemo. useEffect is for syncing outside React - network, DOM APIs, subscriptions - not for computing values from existing state.

Why is my useEffect not updating?

Almost always the dependency array: an empty [] runs the effect once and never re-runs, so it ignores later state changes. Add the values it reads (like the search query) to the deps so it recomputes when they change.

Keep learning

Fix a React useState Stale-State BugFullstack projectFix a Blank React Dashboard (Failed Fetch)Fullstack projectFix the CORS Error Blocking Your API CallsFullstack projectFullstack roadmapStep by step to hiredFullstack interview questionsSTAR answersAll Fullstack projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →