If you've ever seen this ESLint warning:
React Hook useEffect has a missing dependency: 'yourFunction'. Either include it or remove the dependency array.
You're not alone — and you're probably asking:
useCallback()
here?Let’s break this down clearly.
This is a very common pattern:
useEffect(() => {
updateSomething();
}, []); // Run once after mount
This works just fine in React — it's saying:
"Run this effect only once after the initial render."
But ESLint (specifically the react-hooks/exhaustive-deps
rule) might still complain:
"You’re using
updateSomething
inside the effect, but it’s not in the dependency array."
ESLint is trying to protect you from accidentally using stale or unstable references inside effects. It's checking for all variables and functions used inside useEffect
, and if any are omitted from the dependency array, it throws a warning.
It doesn't care whether your effect is supposed to run once — it just checks that all references are declared.
function MyComponent() {
const updateSomething = () => {
// do something
};
useEffect(() => {
updateSomething();
}, []); // no ESLint warning in many cases
return <div />;
}
ESLint can often see that:
So it might not warn you here — but that’s a best-effort guess.
export function useCustomHook() {
const updateSomething = () => {
// logic here
};
useEffect(() => {
updateSomething();
}, []); // ESLint WILL complain
}
In a hook, ESLint has no way of knowing if the function is truly stable.
It assumes the function might capture dynamic state or props.
So it warns you to either:
useCallback()
✅ Note: The name
useCustomHook
is also critical. React and ESLint enforce hook rules only for functions starting withuse
.
If you named itcustomHookFunction
, the hook rules wouldn't apply — but you'd break React's expectations for custom hooks.
useCallback
Wrap the function in useCallback
:
const updateSomething = useCallback(() => {
...
}, []);
useEffect(() => {
updateSomething();
}, [updateSomething]);
Now ESLint sees the function has a stable identity and a clear dependency list.
If you're sure the function is stable and doesn't rely on changing variables, you can just suppress the warning:
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
updateSomething();
}, []);
This is perfectly safe when you know what you're doing — especially for things like DOM access or chart updates that only run once.
React and ESLint apply different rules based on naming:
Function Type | Naming Convention | React Behavior | ESLint Behavior |
---|---|---|---|
Component | Capitalized (e.g. MyComponent ) | Treated as component | Light hook enforcement |
Custom Hook | Starts with use (e.g. useCustom ) | Treated as hook | Strict hook rules applied |
Regular Function | Anything else | Not treated specially | Hook rules not applied |
✅ Custom hooks must start with
use
for React and ESLint to recognize them and apply hook rules properly. Otherwise, callinguseEffect
oruseState
inside them can throw runtime or linting errors.
Situation | Should you use useCallback ? | Will ESLint complain? |
---|---|---|
Function used in useEffect | ✅ Yes, recommended | ✅ Probably |
Defined inside a component | 🤷 Optional | ❌ Might not |
Defined inside a custom hook | ✅ Yes | ✅ Will warn |
Using function once on mount | ❌ Not required | ⚠️ Add comment if needed |
useEffect(() => {}, [])
.useCallback
to be explicit, or suppress the warning if you’re confident it’s safe.React gives you flexibility — ESLint just gives you suggestions. Use both wisely. ✅