react: [react-hooks/exhaustive-deps] eslint --fix breaks the code
Do you want to request a feature or report a bug?
I want to report a bug
What is the current behavior?
eslint --fix trying to break my hook)
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn’t have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:
I have a very simple hook:
import { useEffect, useState } from 'react'
export function useSwitchTab(trigger, tabsAmount, initialState = 0) {
const [currentTab, setTab] = useState(initialState - 1)
useEffect(() => {
setTab(currentTab + 1 >= tabsAmount ? 0 : currentTab + 1)
}, [tabsAmount, trigger])
return [currentTab, setTab]
}
If props tabsAmount or trigger will change, I need to increase currentTab value by 1. It works fine and looks ok for me, but in eslint-plugin-react-hook rule react-hooks/exhaustive-deps will warn here:
React Hook useEffect has a missing dependency: ‘currentTab’. Either include it or remove the dependency array. You can also do a functional update ‘setTab(c => …)’ if you only need ‘c urrentTab’ in the ‘setTab’ call react-hooks/exhaustive-deps
And eslint --fix will break my code like this:
import { useEffect, useState } from 'react'
export function useSwitchTab(trigger, tabsAmount, initialState = 0) {
const [currentTab, setTab] = useState(initialState - 1)
useEffect(() => {
setTab(currentTab + 1 >= tabsAmount ? 0 : currentTab + 1)
}, [currentTab, tabsAmount, trigger])
return [currentTab, setTab]
}
It will add currentTab in deps for useEffect and this will create endless loop.
What is the expected behavior?
Eslint shouldn’t fix this warning with --fix option, It may break the code.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
react: 16.8.5 eslint-plugin-react-hooks: 1.6.0
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 27
- Comments: 20 (3 by maintainers)
@infodusha I understand how eslint-disable works. Problem is in semantic of eslint --fix.
Just imagine situation I have my version of code - it works fine, I pushed my changes to remote. CI executes ‘eslint --fix’ and deploy broken version of code to production (just because this is autofixable warning)
Eslint --fix should never change logic of your code. But in my example eslint with this rule will change logic of my code. I pretty sure that this warning should be not-autofixable with --fix.
@EugeneDraitsev
// eslint-disable-line react-hooks/exhaustive-depswill helpeslint --fixignore this rule on line@behzad888 I understand that my code is not correct from ‘hooks best practice’ point of view, but this code is still valid and will behave ‘as designed’. Often peoples will create hooks, that will be not follow best practices or even documentation.
As I said above and as @gaearon mentioned in 2nd part of his comment:
From my point of view make sense to change level of
react-hooks/exhaustive-depsfromwarningtoerrorand make it non-autofixable to prevent possibility to break user code. (or at least disable autofix by default)Of Course important to add some “suggestions” for user (and IDE), but maybe just an error description will be enough in this case.
@gaearon - I arrived to this thread and similar ones, simply because eslint autofix for
react-hooks/exhaustive-depsrule messed up my code. I believe many others have stumbled within various threads surrounding the same subject.I’ve read threads on eslint regarding support for autofix disable for specific rules - Seems like there are no current solutions except some hacks you can do in your config + CI.
The eslint guys emphasized several times - it is highly recommended that autofix should not alter runtime behavior, let alone break code.
On one hand, we have a very important rule that we want to enforce. On the other, eslint autofix runs as part of our pre-commit hook which hides the fact that such drastic changes might have happened to your code.
This autofix behavior leaves us with two non-optimal options:
I would rather have autofix for this completely disabled by default then being forced to choose one of the two options above. I believe most people will agree, based on the nature of this autofix.
Is there any work being done on configuring autofix for this rule? I haven’t seen any issue on this. Should I open one?
Your code is buggy. Hook must be like that:
Had a similar issue: “The ‘xxx’ function makes the dependencies of useEffect Hook change on every render. To fix this, wrap the ‘xxx’ definition into its own useCallback() Hook”
The autofix then proceeded to wrap my function into useCallback()… without importing it, breaking my app.
Autofix should really be disabled by default.
@infodusha , Thanks for reasonable code fix. Main problem here, that
eslint --fixwill break my version code at all. Eslint shouldn’t break code in any case (it shouldn’t create endless loops in any situation)The warning is not a false positive.
You can see how to fix it here:
https://reactjs.org/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often
In this particular case, the fix would be to function form of
setState, like in the above comment.It’s fair that there is an expectation that
eslint --fixshouldn’t break the code (even if the code is already broken). The root of the issue here is that ESLint offers no way to offer “suggestions”. We’d like a way to indicate that the autofix isn’t guaranteed to work, but is a good starting point for the fix (e.g. for IDE integration that lets you manually apply it by clicking a suggestion icon).We could disable autofix by default, and add an option that enables it. I don’t know if this is what most people want.
Yes, this seriously needs to be addressed. I just added this lint rule to a large codebase and ran autofix to resolve some of the basic issues. My assumption was that the logic would not be changed, as is the reasonable expectation with ESLint fixes. However, after testing my code I discovered that it had been broken in tons of components.
I get that there were technical issues in how the hooks were used, to begin with, which is exactly why there should have just been warning or error raised so that I had the opportunity to address them with the proper scrutiny. But having a potentially breaking fix is unreasonable and renders (what is otherwise a really good linting rule) rule dangerous.
Please please please disable the autofix by default, or remove it entirely. In the meantime, I forsee this causing breaking changes in our codebases as logic is automagically changed during pre-commit hooks.
I have another case to add to this question.
Suppose I have a hook like this:
If I use it in this way (is a simple example so excuse me about the logic, but suppose the useWindowPosition hook returns as the second element of the array a complex function):
Eslint fires the following:
React Hook useEffect has a missing dependency: 'setPosition'. Either include it or remove the dependency array react-hooks/exhaustive-depsWhat is the difference with use:
and why does eslint detect a possible dependency warning but when I use a
nativehook there is no warning?This is resolved now. https://github.com/facebook/react/issues/16313#issuecomment-587149109
@ger86
nativehooks are already into internal git hooks. You also can move your lint into a git hook if you think you need it.We ran into this issue… my team is new to hooks and we’re making all kind of rookie mistakes 😉 so when I turned on the new exhaustive deps rule and ran it through auto fix I had this unpleasant effect of code being utterly broken all over the place. So we have to disable the rule completely because people inadvertently run autofix and break code all over.
We’re going through and fixing all of these instances but yeah having “working” (yes I realize its broken in a sense) code all of a sudden cause fatal failures seems unacceptable for an “autofix”. I don’t think we’ll ever enable this rule with autofix because we can’t trust that it won’t produce a fatally broken build.
@EugeneDraitsev I thought eslint-disabe shoud also disable
eslint --fixGoogled that you can disable autofixig this rule as temporary solution:eslint --fix --rule 'react-hooks/exhaustive-deps: off'