React Hook useEffect has a missing dependency: 'fetchData'.
Either include it or remove the dependency array.
The ESLint exhaustive-deps rule is telling you that your useEffect uses a value that isnβt in its dependency array. If that value changes, the effect wonβt re-run β which might be a bug.
When to Add the Dependency
If the effect should re-run when the value changes, add it:
// β Warning: userId is missing
useEffect(() => {
fetchUser(userId).then(setUser);
}, []);
// β
Effect re-runs when userId changes
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]);
When Functions Cause the Warning
// β fetchData changes every render β infinite loop if added
const fetchData = () => {
fetch('/api').then(r => r.json()).then(setData);
};
useEffect(() => {
fetchData();
}, [fetchData]); // π₯ new function every render
Fix with useCallback:
const fetchData = useCallback(() => {
fetch('/api').then(r => r.json()).then(setData);
}, []);
useEffect(() => {
fetchData();
}, [fetchData]); // β
stable reference
Or move the function inside useEffect:
useEffect(() => {
const fetchData = () => {
fetch('/api').then(r => r.json()).then(setData);
};
fetchData();
}, []); // β
no external dependency
When to Suppress the Warning
Sometimes you intentionally want the effect to run only once, even though it uses a value:
// Run once on mount, even though userId exists
useEffect(() => {
analytics.track('page_view', { userId });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Only suppress when youβre sure the effect shouldnβt re-run. Add a comment explaining why.
Common Patterns
Fetch on mount:
useEffect(() => {
fetch('/api/data').then(r => r.json()).then(setData);
}, []); // β
no dependencies needed
Fetch when a prop changes:
useEffect(() => {
fetch(`/api/users/${id}`).then(r => r.json()).then(setUser);
}, [id]); // β
re-fetches when id changes
Event listener:
useEffect(() => {
const handler = () => console.log('scrolled');
window.addEventListener('scroll', handler);
return () => window.removeEventListener('scroll', handler);
}, []); // β
set up once, clean up on unmount
The Rule of Thumb
- If the effect uses a value and should re-run when it changes β add it
- If the effect uses a function β move it inside or wrap with
useCallback - If youβre sure it should only run once β suppress with a comment explaining why