Skip to content

Conversation

duncanbeevers
Copy link
Contributor

@duncanbeevers duncanbeevers commented Dec 9, 2020

Ensure symmetric value and setter variable names are destructured from React.setState hook calls

Towards #2417

@karlhorky feel free to riff on this, or let me know how this compares to your own proof-of-concept.

  • Supports both useState and React.useState
  • Tested with TypeScript parser and useState<> type annotation
  • Robust against weird useState uses like destructuring too many items, or not destructuring at all
  • Fixable when 2nd destructured arg does not matching set<X> naming convention

@ljharb I included this in the Best Practices category, though perhaps a different categorization would be more appropriate.

Let me know if the documentation needs anything else, like more examples, or whether the laissez-faire approach to weird assignment is too forgiving.

@duncanbeevers duncanbeevers changed the title [New] : symmetric names [New] : Symmetric setState hook variable names Dec 9, 2020
@karlhorky
Copy link
Contributor

Awesome @duncanbeevers ! I think you've probably put more effort into this than I have up until now - haven't found the time to learn what I would need to in order to tackle this 😬

I think it's looking good 🙌

@ljharb what are your thoughts?

@duncanbeevers duncanbeevers force-pushed the hook-set-state-names branch 5 times, most recently from fd0f854 to b244528 Compare December 11, 2020 21:31
@duncanbeevers duncanbeevers changed the title [New] : Symmetric setState hook variable names [New] : Symmetric useState hook variable names Dec 12, 2020
@duncanbeevers duncanbeevers force-pushed the hook-set-state-names branch 3 times, most recently from 52df451 to 355ce77 Compare December 15, 2020 22:37
Comment on lines 12 to 18
const [color, updateColor] = React.useState();
```

Examples of **correct** code for this rule:

```js
const [color, setColor] = React.useState();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would someone want to configure "set" to make "update" allowed? Should we allow that to be configurable?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you seen this a lot before? I haven't seen this pattern before.

I suppose allowing arbitrary configuration of the prefix would be ok though (eg. a setterPrefix option?) - would allow for flexibility for some teams.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would someone want to configure "set" to make "update" allowed? Should we allow that to be configurable?

Just as the naming of hooks is already opinionated, useXYZ, all the React docs consistently use [thing, setThing].
I think it's best to lean on this convention here.

code: 'const useStateResult = useState()'
},
{
code: 'const [, , unused] = useState()'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like it should be an error, but i guess not from this rule

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, in my workflow this issue would be caught by the type system.
This test is intended to indicate the rule's non-opinion around a third argument (and to make sure I didn't throw an error on weird inputs)

Copy link
Contributor Author

@duncanbeevers duncanbeevers Jan 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now an error. (not fixable)

code: 'const [, setColor] = useState()'
},
{
code: 'const useStateResult = useState()'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about if someone does const [state, setThatState] = useStateResult; or const state = useStateResult[0]; const setThatState = useStateResult[1]; on the next lines?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This rule only looks for calls to useState which are directly destructured+assigned;

These intermediate-value forms aren't recognized by this rule.

Copy link
Contributor Author

@duncanbeevers duncanbeevers Jan 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now an error as well. (not fixable)

Comment on lines 53 to 60
{
code: 'const [color, setColor] = useState<string>()',
parser: parsers.TYPESCRIPT_ESLINT
},
{
code: 'const [color, setColor] = useState<string>(\'#ffffff\')',
parser: parsers.TYPESCRIPT_ESLINT
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's also duplicate these and add parsers['@TYPESCRIPT_ESLINT'] so we can handle the new parser too. grep for parsers.TS( to find examples of how to do this safely.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I added these correctly.

message: 'setState variable names are not symmetric, setter should be setColor'
}],
output: 'const [color, setColor] = useState<string>()',
parser: parsers.TYPESCRIPT_ESLINT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicate test case with the new parser here too

@ljharb ljharb marked this pull request as draft December 30, 2020 04:14
@duncanbeevers duncanbeevers marked this pull request as ready for review December 30, 2020 21:20
@duncanbeevers duncanbeevers requested a review from ljharb December 30, 2020 23:08
Ensure two symmetrically-named variables are destructured from useState hook calls
@duncanbeevers
Copy link
Contributor Author

duncanbeevers commented Jan 4, 2021

@ljharb The scope of this rule has increased, and its naming has been changed from hook-use-state-names to the "generic" hook-use-state.

As you mentioned in your first review, certain unusual usages of setState weren't addressed by this rule.
In addressing these issues the other comments raised in this PR, the prescriptive nature of this rule seemed clearer.

Towards that end of providing gentle, but clear guidance in the usage of this hook+rule, the naming has been changed to hook-use-state.

It is now a bit more prescriptive, and therefore a bit more helpful in its fix suggestions.

Stuff that didn't change

  • there is no configuration
  • all previously-invalid cases remain invalid (invalid cases were focused solely on variable naming violations)

Stuff that changed

  • enforced destructuring of useState call to value+setter
  • added fix for mis-destructured assigments when value variable name is present
    • setter variable name is inferred from value variable name
    • over+under arity destructures, and mis-named destructures are all fixed by replacement with canonicalized 2-arity form
  • added tests for pathological cases
// Who does this!?
const [] = useState();
const [, , , , ] = useState();

@duncanbeevers duncanbeevers changed the title [New] : Symmetric useState hook variable names [New] : Enforce useState hook destructuring and variable naming Jan 4, 2021
@duncanbeevers
Copy link
Contributor Author

Closing now that the core rule change has landed.
eslint/eslint#14012

@duncanbeevers duncanbeevers deleted the hook-set-state-names branch February 10, 2021 16:30
@duncanbeevers duncanbeevers restored the hook-set-state-names branch February 10, 2021 16:33
@ljharb ljharb added the eslint label Feb 10, 2021
@duncanbeevers
Copy link
Contributor Author

Oops wrong PR. I'll re-open this one and remove the no-unused-expresssion PR.

mea culpa

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants