-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
[New] Symmetric useState hook variable names #2921
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…riable names Ensure two symmetrically-named variables are destructured from useState hook calls
i would have strongly preferred reopening #2873 instead of leaving it permanently orphaned on the repo forever :-/ please strive to avoid opening unnecessary PRs. |
Same; sorry about the premature close+delete. After I restored the branch, I wasn't able to re-open the PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about React.useState
calls?
Those are covered in the documentation and in the tests; {
code: 'const [color, setColor] = React.useState()'
}, |
01e6a4d
to
2bfe443
Compare
I've added some primitive support for ensuring that React / useState is imported (though not necessarily in-scope). import ReactAlternative from 'react'; // React detected, exotic local name
import { useState as useStateAlternative } from 'react'; // React.useState detected, exotic local name I tried using the ScopeManager to determine at the I've never seen a |
Could we add some tests for |
@duncanbeevers just circling back around to this one - do you think that you'll be able to proceed with this PR? |
Yeah, I'll take another pass at this. I'm seeing some unexpected failures in the TS parsing. It has been a while since I dusted this off and as I'm bringing it into alignment with the Example {
code: `import { useState } from 'react';
const [color, setColor] = useState<string>()`, // THIS DOES NOT PARSE
features: ['ts'],
},
{
code: `import { useState } from 'react';
const [color, setColor] = useState<string>('#ffffff')`, // THIS PARSES
features: ['ts'],
}, Error Message
Am I using the wrong Nevertheless, I'll carry on with expanding test test cases I can get working. |
24ec0b3
to
c00ac9a
Compare
No, but the ts feature runs on both the old and new TS parsers - it’s fine to add a |
Codecov Report
@@ Coverage Diff @@
## master #2921 +/- ##
==========================================
+ Coverage 97.51% 97.53% +0.01%
==========================================
Files 119 120 +1
Lines 8180 8245 +65
Branches 2934 2967 +33
==========================================
+ Hits 7977 8042 +65
Misses 203 203
Continue to review full report at Codecov.
|
I've updated the specs to use the new |
lib/rules/hook-use-state.js
Outdated
create(context) { | ||
let reactImportLocalName; | ||
let reactUseStateLocalName; | ||
|
||
return { | ||
CallExpression(node) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should all be done inside a Components.detect call, to ensure it only runs on things that are detected as SFCs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I wrapped the rule definition in Components.detect
tests/lib/rules/hook-use-state.js
Outdated
}, | ||
{ | ||
code: `import { useState } from 'react'; | ||
const [color, setFlavor] = useState()`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useState
can only be called inside an SFC that React renders (or inside another hook), so i don't think these tests are valid unless they're inside a component or inside a custom hook
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you feel that these cases as-written should be flagged as invalid by this rule?
Or should I just add the dressing to these test cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As written, the rule should ignore them, because they're not actually a valid hook call - it will throw at runtime already.
Also, eslint-plugin-react-hooks will, i believe, warn on it as-is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i thought we decided this should be ignored? or did we decide instead that its placement should be ignored.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eslint-plugin-react-hooks#rules-of-hooks
is almost entirely dedicated to detecting
I see a few options here.
- Do nothing; flag this case as invalid, and rely on
eslint-plugin-react-hooks
to flag the bad usage - Do something simple, like ignore top-level invocations of the hook, which solves this test case, but is totally cheating
- Do something ambitious like port (most of) FBs RulesOfHooks over to this repo
- Do something ambitious like integrate FBs RulesOfHooks into
util/Components
- Do something ambitious like extract FBs RulesOfHooks into a new module that can be shared by both repos
- Do something even more ambitious which I haven't thought of.
I'm in favor of Do nothing
The price of a mis-flag here is low; neither this rule nor eslint-plugin-react-hooks#rules-of-hooks
attempt to fix this situation (suggestions-only), so it doesn't add any extra fixer passes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked out copying over pieces from rules-of-hooks, but its architected a bit differently.
Whereas this rule relies solely on visiting CallExpression
nodes, rules-of-hooks
:
- accumulates info about
CallExpressions
- and then does its heavy-lifting, including checking the
isInsideComponentOrHook
status when the rule'sonCodePathEnd
callback fires.
I wrapped most test-cases in a little more realistic code; a custom hook that acts as pass-through for the useState return value. |
012cde0
to
0f13a4e
Compare
ecb6808
to
b344dfc
Compare
Are there other hooks that might want naming guidelines? |
b344dfc
to
292bbbb
Compare
Seems pretty good overall. |
186578c
to
1d81d76
Compare
I certainly use some naming conventions, but in my experience there's nothing as universally-used as this Here are some conventions I use regularly, just as a concrete example of stuff I would enforce with a lint rule. These are simply personal preferences, and I don't feel they represent common practice.
|
Fair - I'm more thinking that if there were ever to be anything in the future, it'd be nicer to add it as an option to a single hook naming rule than to have to make a whole new rule. |
Config / Other HooksYes, I tried to consider some real-world cases, and how this rule might accommodate those conventions, but I couldn't come up with anything that was both compelling, and that felt amenable to being expressed here alongside this rule. One thing I really like about this rule as it stands is zero-configuration. If naming (or other) conventions emerge around other hooks in the future, adding new rules for those hooks while keeping the config for this one clean seems like a reasonable approach. This code supporting this Use in the WildI hooked this rule up to a couple of work repositories; as expected I saw a few usages flagged. Somewhat unexpected was that it didn't crash on anything! (pats self on back) The automatic variable renaming works, but falls short of a complete refactor. Here's a small example. Screen.Recording.2021-12-12.at.9.43.42.AM.movThis behavior makes me feel that all the fixes should be changed to suggestions, since I can't reliably rename later usages of the variable. Maybe there's an easy way to do this properly, but if so I feel that Next StepsLet me know which way you'd like to see this go. If I don't hear anything right away, I'll probably convert the fixes to suggestions, and leave the larger variable-rename-refactor work for another PR. |
I agree such a thing would need to be a shared helper, and i agree that they shouldn’t be autofixes if they’re going to leave things in a broken state. Your plan seems fine. |
1d81d76
to
d032842
Compare
Okay, this rule is now suggestions-only, including the Specs are updated, and the internal implementation is simplified since everything is suggestions now (no more fix/suggest split) |
30ef2b8
to
1252497
Compare
1252497
to
0eba9ec
Compare
I think the next step for this might be to actually do the correct variable rename within its scope. It seems like a a chunk of work, but maybe there's something already out there in the eslint ecosystem to accomplish this. I saw this comment about the general technique of expanding the fix range to cover the entire scope of the changed variable. |
tests/lib/rules/hook-use-state.js
Outdated
}, | ||
{ | ||
code: `import { useState } from 'react'; | ||
const [color, setFlavor] = useState()`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i thought we decided this should be ignored? or did we decide instead that its placement should be ignored.
72e3f64
to
c5b65a9
Compare
I rebased this with some tweaks, and there's a failing test - namely, the one where it should ignore hook calls outside of components (unless we agreed not to treat those differently, in which case, please point me to that thread). Thanks! |
Implementing this (ignoring hook usages in non-valid contexts) is equivalent to a large portion of the work I replied in this comment as to why I think I think the best option is not to implement such functionality for this rule, at least today. |
35077ca
to
4f54108
Compare
Ensure two symmetrically-named variables are destructured from useState hook calls
Discussion
This is a re-opening of #2873
I've rebased and updated the README using
npm run generate-list-of-rules
to follow the new table-ish layout.useState
andReact.useState
useState<>
type annotationuseState
uses like destructuring too many items, or not destructuring at allset<X>
naming conventionFixes #2417.