This post is inspired by the conversation on this github issue which was shared by a colleague in one of our technical meeting. There are so many other cases discussed here so its worth reading.
Rationale
While working with a react hook, you must have been in a situation when you get this linting warning react-hooks/exhaustive-deps
. You try to add the dependency to the array and may ended up in an infinite loop or any unexpected behavior;
For example:
You have a state count in your component and you want to increment it only once. Most likely you will write in this way.
javascript
useEffect(() => {
setCount(count + 1);
}, []);
This will give you a warning that React Hook useEffect has a missing dependency: 'count'. Either include it or remove the dependency array
You have an easy exit: that is to ignore the error by disabling the rule;
eslint-disable-next-line
javascript
useEffect(() => {
setCount(count + 1);
// eslint-disable-next-line
}, []);
The design of useEffect
forces you to confront it. You can of course work around it, but the default is to nudge you to handle these cases. Instead of disabling these warnings, I've found it worth finding a way to accommodate the dependency array requirements.
In most situations its practically possible and I will talk about some very common use-cases here.
For state dependencies
In the preceding example, you can get rid of that warning using a callback in your setCount
method because count will no longer be a dependency 😊
javascript
useEffect(() => {
setCount((count) => count + 1);
}, []);
Another drawback off adding
// eslint-disable-next-line
could be that it will not warn you about future dependencies. i.e If you made some changes in that hook which has a dependency but you forgot to add it in the dependency array, you will not get a warning now as you already have disabled that. This could lead to unexpected behavior and even break your app.
For function dependencies
When you use any function in the useEffect
hook.
Let’s consider you have a function that uses name
as argument and does something. You will get an es-lint
warning for not adding the function in the dependency array.
javascript
const functionName = (name) => {
// do something
};
useEffect(() => {
functionName(name);
}, [name]);
But, even adding it directly to the dependency wont fix it and you will get another warning that you have just caused an infinite loop because someFunction has a different reference on each render
. So whats the fix?
The solution here is likely to wrap that function in a useCallback
hook because useCallBack
will return a memoized callback. It makes certain that the function doesn’t change references between renders.
javascript
const functionName = useCallback((name) => {
// do something with count
}, []);
useEffect(() => {
functionName(name);
}, [name, functionName]);
PS:
As per official useCallBack docs make sure that every value referenced inside the callback should also appear in the dependencies array.
Here is another use-case I saw on the same github issue related to useCallBack, where a user will add an email and invite those email to the app. It potentially fix the deps into [emails, props]
. Whereas, actually it needs to update the function reference only when onSubmit
change and not any other prop
.
javascript
const handleSubmit = React.useCallback(
() => {
props.onSubmit(emails);
},
[emails, props]
);
This is because technically props.onSubmit() passes props itself as
this
toonSubmit
call. SoonSubmit
might implicitly depend on props and the best practice is always de-structuring.
Valid-Case
I also want to talk about one of the legitimate cases that I found on the same thread, where you may have to disable extraneous dependency warning
javascript
useEffect(() => {
window.scrollTo(0, 0);
}, [activeTab]);
Summary
Its very common to disable/ignore react-hooks/exhaustive-deps
linting warnings in most cases. Probably you actually want to fire the hook when the dependency changes. It might not seem like it at first, but there are probably some edge cases for which your effect logic will fail if you don’t include the variable in the dependency array. So, you should try to fulfill react hook dependency requirements with given ways instead disabling.
Top comments (1)
With react version 19 there will not be a need for useCallback() hook because the react compiler will take care of it. So i think the react team are already aware about it and in react 19 they will fix this exhuastive usage of useMemo and useCallback everywhere in your codebase.