React Hook useEffect Has a
Missing Dependency — ESLint Warning Explained
React 19 • Next.js 15 • TypeScript • Vite • React Router • Redux Toolkit
📝 What Does “React Hook useEffect Has a Missing Dependency” Mean?
The “React Hook useEffect has a missing dependency” warning
is one of the most common ESLint warnings you'll encounter in React development.
It appears when the useEffect hook uses a variable or function that
isn't listed in its dependency array.
The full warning message reads:
React Hook useEffect has a missing dependency: 'someVariable'.
Either include it or remove the dependency array.
Why does this warning exist? The exhaustive-deps
ESLint rule ensures that your useEffect hooks include all values
they depend on. This prevents:
- Stale closures — Using outdated values from previous renders.
- Missing re-runs — Not updating when dependencies change.
- Hard-to-debug bugs — Silent failures that only appear in specific scenarios.
- Infinite loops — When state updates rely on stale values.
⚡ Quick Reference — Common Solutions at a Glance
[variable].useCallback to stabilize references.useMemo.setState(prev => ...).📋 Golden Rules for useEffect Dependencies
- Include all values used in the effect — State, props, context, and any derived values.
-
Use
useCallbackfor functions — Functions passed as dependencies should be memoized. -
Use
useMemofor derived values — Computed values used in the effect. -
Use functional updates for state —
setState(prev => ...)to avoid dependency on state. - Don't ignore the warning — Understand why it appears and fix it properly.
-
Use
eslint-disable-next-linesparingly — Only when the warning is truly a false positive.
🧩 Detailed Breakdown — Every Scenario Solved
Each card below dives deep into one specific scenario involving missing dependencies, with root causes, code examples, and step-by-step fixes.
🛡️ Prevention — Avoid Missing Dependency Warnings
exhaustive-deps RuleInstall
eslint-plugin-react-hooks and enable the rule in your ESLint config.
Ask yourself: "What values does this effect depend on?" List all of them.
useCallback for FunctionsAny function used in
useEffect should be memoized with useCallback.
useMemo for Computed ValuesDerived values used in effects should be memoized to prevent unnecessary re-renders.
useReducer for Complex StateReducer actions are stable and can reduce dependency issues in effects.
use hook for promises
handles data fetching differently and may not trigger the same dependency warnings
as useEffect.
❓ Frequently Asked Questions
Ignoring the warning can lead to stale closures — your effect will use outdated values from the first render, even if the values have changed.
For example, if your effect depends on userId but you
don't include it in the dependency array, the effect will always use
the initial userId, even after it changes.
Yes, in most cases. The exhaustive-deps
rule is designed to help you catch bugs. Adding all dependencies is
the safest approach.
However, there are exceptions:
- Functions from
useCallback— If the function is memoized, it's stable. - Dispatch from
useReducer— The dispatch function is stable. - Refs —
ref.currentchanges don't trigger re-renders.
In these cases, you can safely ignore the warning or use
eslint-disable-next-line with a comment explaining why.
No. React 19 maintains the same dependency rules
as React 18. The exhaustive-deps ESLint rule still applies
to useEffect and useLayoutEffect.
React 19's new use hook for promises works differently
— it doesn't have the same dependency requirements, which can be a
nice alternative for data fetching.
When a function is used in useEffect, you have two options:
- Wrap it in
useCallback— This stabilizes the function reference. - Move it inside the effect — If the function is only used in that effect.
// ✅ Option 1: useCallback
const fetchData = useCallback(() => {
// ... function logic
}, [deps]);
useEffect(() => {
fetchData();
}, [fetchData]); // ✅ Stable dependency
// ✅ Option 2: Move inside effect
useEffect(() => {
const fetchData = () => {
// ... function logic
};
fetchData();
}, [deps]);
useCallback and useMemo?
The same rule applies! useCallback and
useMemo also require exhaustive dependencies. If you use
a variable inside them and don't list it, ESLint will warn you.
// ❌ Wrong — missing dependency in useCallback
const handleClick = useCallback(() => {
console.log(count); // Uses count
}, []); // 👈 Missing count!
// ✅ Correct — include count
const handleClick = useCallback(() => {
console.log(count);
}, [count]); // ✅ count in deps
Always include all values used inside useCallback and
useMemo to avoid stale closures.
eslint-disable-next-line?
Only in specific cases where the warning is a false positive.
Acceptable cases:
useReducerdispatch function (stable).useRefobject (stable, changes don't trigger re-renders).setStatefunction fromuseState(stable).- When you intentionally want the effect to run only once.
Always add a comment explaining why:
// ✅ Acceptable — dispatch is stable
useEffect(() => {
dispatch({ type: 'LOAD_DATA' });
}, [dispatch]); // ✅ dispatch is stable
// ✅ Acceptable — intentional once-only effect
useEffect(() => {
// One-time initialization
}, []); // eslint-disable-line react-hooks/exhaustive-deps
eslint-disable
as a lazy fix. Always understand why the warning appears before disabling it.
0 Comments
thanks for your comments!