How to fix React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps in React
ESLint warning from the exhaustive-deps rule indicating that a variable, prop, or state value referenced inside useCallback is not included in its dependency array. This can lead to stale closures and bugs where the callback uses outdated values.
Quick fix
Fixes React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
// Before (causes warning)
const handleClick = useCallback(() => {
console.log(count); // count is used but not in deps
}, []);
// After (warning fixed)
const handleClick = useCallback(() => {
console.log(count);
}, [count]); // count added to dependency array- Add the missing dependency to the dependency array
- Memoize function dependencies with useCallback
- Memoize object or array dependencies with useMemo
- Use functional updates for state dependencies
// Before (causes warning)const handleClick = useCallback(() => {console.log(count); // count is used but not in deps}, []);// After (warning fixed)const handleClick = useCallback(() => {console.log(count);}, [count]); // count added to dependency arrayReact Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
What this error means
This ESLint warning comes from the `react-hooks/exhaustive-deps` rule, which is part of the official `eslint-plugin-react-hooks` package maintained by the React team. The rule analyzes your `useCallback` hook and verifies that every value referenced inside the callback function is also listed in the dependency array. When you use `useCallback`, React memoizes the function and only creates a new function instance when one of the dependencies changes. If you reference a variable, prop, or state value inside the callback but don't include it in the dependency array, the callback will continue using the value from when it was first created—not the current value. This creates a "stale closure" where your callback operates on outdated data. The exhaustive-deps rule exists to prevent subtle bugs that are difficult to debug. While it's tempting to disable the warning, doing so often leads to race conditions, incorrect behavior, and unpredictable component state.
Typical symptoms
- ESLint warning: "React Hook useCallback has a missing dependency"
- Yellow or red underline in your code editor under useCallback
- Callback function uses outdated values from props or state
- Component behaves incorrectly when props or state change
- Event handlers reference stale data
- Callbacks passed to child components don't reflect current state
Common causes
- Using props, state, or context values inside useCallback without including them in the dependency array
- Referencing functions from the parent scope that aren't memoized with useCallback
- Accessing object properties or array elements that change over time
- Using variables from outer scopes without declaring them as dependencies
- Intentionally creating a callback that should only capture initial values but not communicating this to ESLint
How to fix "React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps"
The most straightforward fix is to include the missing dependency in the array:
// Before (causes warning)
const handleClick = useCallback(() => {
console.log(count); // count is used but not in deps
}, []);
// After (warning fixed)
const handleClick = useCallback(() => {
console.log(count);
}, [count]); // count added to dependency arrayThis ensures the callback always has access to the current value of count. The callback will be recreated whenever count changes.
If you're referencing another function inside useCallback, wrap that function in its own useCallback to maintain a stable reference:
// Before (causes warning)
const fetchData = () => {
api.getData(userId);
};
const handleRefresh = useCallback(() => {
fetchData(); // fetchData recreated every render
}, []);
// After (warning fixed)
const fetchData = useCallback(() => {
api.getData(userId);
}, [userId]);
const handleRefresh = useCallback(() => {
fetchData(); // stable reference
}, [fetchData]);This creates a chain of properly memoized functions where each declares its dependencies correctly.
If your callback depends on an object or array, memoize it with useMemo to prevent unnecessary recreations:
// Before (causes warning)
const config = { userId, apiKey };
const handleSubmit = useCallback(() => {
api.send(config); // config is a new object every render
}, []);
// After (warning fixed)
const config = useMemo(() => ({
userId,
apiKey
}), [userId, apiKey]);
const handleSubmit = useCallback(() => {
api.send(config);
}, [config]);Without useMemo, the object is recreated on every render, causing useCallback to see it as a "changed" dependency.
When your callback only needs to update state based on the previous value, use the functional update form to avoid depending on the state variable:
// Before (requires count in deps)
const increment = useCallback(() => {
setCount(count + 1);
}, [count]);
// After (no dependency needed)
const increment = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []);This pattern is especially useful for callbacks that should have a stable reference but need to update state.
If you need to access the latest value of something without causing the callback to be recreated, store it in a ref:
const countRef = useRef(count);
useEffect(() => {
countRef.current = count; // Always keep ref updated
}, [count]);
const handleClick = useCallback(() => {
console.log(countRef.current); // Access latest value
}, []); // Empty deps - callback never recreatedThis is useful when you want a stable callback reference but need access to current values. Use this pattern sparingly and only when you understand the tradeoffs.
In rare cases where you intentionally want to capture the initial value and ignore updates, disable the rule with a comment explaining why:
const handleMount = useCallback(() => {
// We intentionally only want the initial userId value
// eslint-disable-next-line react-hooks/exhaustive-deps
console.log(userId);
}, []);Always add a comment explaining why the rule is disabled. This helps future maintainers understand the intentional deviation. In most cases, one of the solutions above is preferable.
Advanced notes
Why the exhaustive-deps rule exists
The React team created this rule because stale closures are one of the most common sources of bugs in React applications. When a callback "closes over" a variable but doesn't declare it as a dependency, the callback continues using the value from when it was created, not the current value. This leads to subtle bugs that are hard to reproduce and debug.
Performance considerations
Some developers worry that adding dependencies will cause too many re-renders. However, if your callback needs the current value of a dependency, it *should* be recreated when that value changes—that's correct behavior. If you're concerned about performance:
1. Measure first—premature optimization causes more bugs than it solves
2. Use React DevTools Profiler to identify actual performance bottlenecks
3. Consider if you actually need useCallback at all—it's only beneficial when passing callbacks to memoized child components
React 19 and the React Compiler
React 19 introduces an experimental compiler that can automatically handle memoization, potentially reducing the need for manual useCallback usage. However, the exhaustive-deps rule will remain important for ensuring correct dependencies even when the compiler handles the memoization.
ESLint autofix
The exhaustive-deps rule includes an autofix feature. In VS Code or other editors with ESLint integration, you can often press Ctrl+. (or Cmd+.) on the warning and select "Fix this exhaustive-deps warning" to automatically add the missing dependencies. Always review the autofix to ensure it makes sense for your use case.
Dependency array complexity
If you find yourself with a useCallback that has 5+ dependencies, consider whether:
- The function is doing too much and should be split up
- Some dependencies could be combined into a single object
- The callback actually needs to be memoized at all
Complex dependency arrays are often a code smell indicating the component has too many responsibilities.
Frequently asked questions
- What does "React Hook useCallback has a missing dependency: 'variable'. Either in" mean?
- This ESLint warning comes from the react-hooks/exhaustive-deps rule, which is part of the official eslint-plugin-react-hooks package maintained by the React team. The rule analyzes your useCallback hook and verifies that every value referenced inside the callback function is also listed in the dependency array. When you use useCallback, React memoizes the function and only creates a new function instance when one of the dependencies changes.
- How do I fix "React Hook useCallback has a missing dependency: 'variable'. Either in"?
- ESLint warning from the exhaustive-deps rule indicating that a variable, prop, or state value referenced inside useCallback is not included in its dependency array. This can lead to stale closures and bugs where the callback uses outdated values.
- What causes "React Hook useCallback has a missing dependency: 'variable'. Either in"?
- Using props, state, or context values inside useCallback without including them in the dependency array Referencing functions from the parent scope that aren't memoized with useCallback Accessing object properties or array elements that change over time
- Is the "React Hook useCallback has a missing dependency: 'variable'. Either in" error serious?
- It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.