-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
sort-comp Allow methods to belong to any matching group. #1858
Conversation
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.
Thanks, I think this fix is great, and is effectively semver-patch.
lib/rules/sort-comp.js
Outdated
matching = new RegExp(isRegExp[1], isRegExp[2]).test(method.name); | ||
} else { | ||
matching = methodsOrder[i] === method.name; | ||
switch (currentGroup) { |
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.
does this have to be a switch? it's so much more verbose than if/elses would be.
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.
It doesn't have to be a switch.
Generally switch
is considered to be more readable and more performant than a chain of if
/else
es. For me, the potential conciseness of the if
/else
chain wouldn't help with readability or maintainability; which would be my main concern.
I don't know the performance characteristics for this in JavaScript, but this article would indicate that it's true for JavaScript as well.
The fallthrough block of lifecycle methods is the most verbose part of this. That could be changed to be a part of the default
block, which would allow for the shared array of lifecycle method names (mentioned in another comment) with an .includes(currentGroup)
. I would say that this is less "pure" than the code as it stands, but not enough to block the suggestion.
Of course, you may just want it to be all if
/else
.
Let me know which option you want to go with.
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 definitely disagree; I consider switch to almost always be less readable than an alternative, and certainly less maintainable. It'd be great to go with all if/else.
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 will update to be if
/else
chain.
I would be remiss if I didn't point out that your opinion goes against the popular opinion. e.g. why switch case and not if else if So while this change may be more readable and maintainable for you, it potentially wont be for the masses.
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.
Done.
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.
Hi Jordan. It's a shame we have to belabour this point.
I didn't provide JavaScript specific discussions because you didn't specifically mention JavaScript in your opinion. Taking that into consideration is it fair to read your original statement as:
I definitely disagree; I consider switch to almost always be less readable than an alternative, and certainly less maintainable for JavaScript. I agree that it is more readable, maintainable, and performant for other languages. This is the overwhelming shared opinion within the JavaScript community, despite the similarities in syntax between JavaScript and other languages, and the similar performance implications with other languages.
If so, can you please provide references as I'd like to be following community best practices.
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.
Since we're talking about a JS project, the context "in JavaScript" should be assumed. I offer no opinion here on use of switch
in languages irrelevant to this repository (namely, everything that's not JavaScript).
Google, as well as common style guides (jslint, for example, since the 90s; airbnb's; etc), can all be consulted for information on that.
- https://javascriptweblog.wordpress.com/2010/03/08/caseagainstswitch/
- https://ericleads.wordpress.com/2012/12/11/switch-case-considered-harmful/
- https://hackernoon.com/rethinking-javascript-eliminate-the-switch-statement-for-better-code-5c81c044716d
- https://medium.com/tandemly/whats-wrong-with-the-switch-statement-in-javascript-c560e8ea3c0b
- https://codeburst.io/alternative-to-javascripts-switch-statement-with-a-functional-twist-3f572787ba1c
etc.
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.
You mention styleguides, but only link to one person opinion pieces.
The styleguides themselves make no claims that switch
should be avoided and instead encourage sane syntax as per every other language that has switch
statements.
The opinion pieces that you link to make the main complaints that switch doesn't follow their preferred programming paradigm. I.e. it isn't OO enough (very appropriate for a 2010 opinion piece.), or not functional enough (the new hotness). JavaScript is not a single paradigm language.
You say that JSLint has discourage the use of switch
since the 90s, but considering it was created in 2002, and Crockford himself doesn't advocate avoiding switch
I'm not sure what to make of your claims.
Others stating that premature deoptimisation is preferable to using using the actual features of a language so that they can pretend it's a different language doesn't seem rational to me. Using JavaScript as JavaScript with the lint enforced best practices does, and the styleguides appear to back this view.
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.
ah lol, you're right about jslint, i misremembered.
Either way, avoiding switch statements is still a best practice (in JavaScript, obviously).
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.
Can you please provide sources that confirm it is best practice to avoid switch statements in JavaScript, or help me understand what is incorrect about my last message if you believe your previous sources were sufficient.
lib/rules/sort-comp.js
Outdated
indexes.push(setterIndex); | ||
} | ||
} | ||
for (let groupIndex = 0; groupIndex < methodsOrder.length; groupIndex++) { |
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.
could this be a .map
instead of a for loop?
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 will update this to be a forEach
(since we don't care about the resulting array that a map
creates).
I had considered making it a forEach
, but reading the code I had assumed that for
s were preferable since there's a lot of places that forEach
could be used but is not.
lib/rules/sort-comp.js
Outdated
} | ||
break; | ||
} | ||
case 'displayName': |
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 seems like a list of lifecycle methods that should be a shared constant array, available in multiple rules
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 think this is a sane suggestion, but...
It only works if you want to move to one of the two options above where we remove the lifecycle switch block (or switch
completely).
And there is code that can make use of the shared array. (After a quick and rudimentary search I'm not seeing any other candidate code, but it may exist.)
d7417ea
to
807073c
Compare
'getChildContext', | ||
'getDerivedStateFromProps', | ||
'componentWillMount', | ||
'UNSAFE_componentWillMount', |
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 think the UNSAFE_ methods should only be included in this array when the React version in settings is 16.3+ - same with gDSFP, gSBU, and cDC (for 16.0+)
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 disagree with this. In my opinion this makes sense in the config code, but not in the rule implementation itself.
If someone has their config set up so that it includes the UNSAFE_
methods and they have the UNSAFE_
methods in their component then eslint should enforce that config no matter which version of React they are using.
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.
If someone is using React 15, React 16 lifecycle methods should not be sorted in the lifecycle group - because for them, they aren't lifecycle methods. Similarly, if someone is using React 17, where componentWillMount
theoretically does not exist, cWM should not be sorted in the lifecycle group - because for them, it's not a lifecycle method.
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.
Agreed. This is a concern for the code that loads the default config.
This code is dealing with the finalised config. It should not undo a config based on assumptions about the environment. eg the user may have their own UNSAFE_
method that just happens to match the choice a new version of React implements. One would hope this is unlikely, but it would be bad practice to assume it impossible.
Assuming we even wanted to try and undo the config, because custom groups are flattened in the config, and someone could overwrite the lifecycle group, we could only undo methods that were not contiguous with other lifecycle methods. Even then we would be assuming that the user used the lifecycle group when they may have hand coded the list. We could inspect the raw config and deconstruct from there... Why are we trying to undo the users config?
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 guess i'm confused why we need a separate list of lifecycle methods, if this is just "the user's config". should this not fall down to line 200?
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.
Yes, line 162 is a functional subset of 200. However 162 is documented behaviour and 200 is not. As stated in the PR description, I've added a test to make sure the functionality isn't lost, but presumed it was better to code to expected behaviours.
Would you prefer that I comment L#200 making it explicit that it's undocumented functionality, or remove 162 and effectively hide (in my opinion) documented behaviour in the code for undocumented behaviour? Of course the tests would catch if someone removed the code thinking they were removing the undocumented behaviour, but that feels too obfuscated to me.
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.
Hmm.
Let's keep this as-is; thanks for your explanation.
5d46d16
to
c6bb825
Compare
lib/rules/sort-comp.js
Outdated
'componentDidCatch', | ||
'componentWillUnmount', | ||
'render' | ||
].includes(currentGroup)) { |
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.
includes
is not available on node 4; please use the array-includes
package instead of relying on the native method.
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.
Done.
c6bb825
to
76a4120
Compare
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.
LGTM, thanks!
I'm just wanted to calibrate my expectations with this PR. Is there a rough estimate on when it will be merged/released? Is there anything more I can do to help progress it? |
v7.12.0 is released. |
Fixes #1821. Fixes #1838. Fixes #1808. Also fixes a no-issue bug (see bottom in bold).
The history of the rule implies that it was intended that methods matching multiple groups should be able to be placed in any of those groups. See initial commit of the rule.
It seems over time that restrictions have come into the code that prevent this. I didn't find any discussions about this change in behavior. This change has lead to multiple bugs because the config isn't refined enough to accommodate individual preferences.
This PR solves those bugs by reintroducing the ability for methods to be grouped with any matching group.
For example:
Static methods that match a RegExp can be placed with
static-methods
or with the RegExp group.Static lifecycle methods can be placed with
static-methods
or with lifecycle methods.Static getters can be placed with
static-methods
orgetters
.This allows teams with different preferences to put the method where they think is appropriate, but it means that individuals within a team can still place methods in locations that the team doesn't like and this will need to be a manual check on code review.
This seems like the best compromise until team preferences can be fully supported by the rules options.
About the implementation:
The methodOrder is now processed in array order. This was originally done because I took the approach that the methodOrder should determine stricter behavior. While I no longer believe that to be the solution, I believe this code layout is more readable than previously, and there are fewer loops now than previously. (Previously not all loops were executed, but that would have had to change in this PR.)
I moved the test descriptions into the
code
blocks, so that when there is a test error you actually know which test has failed. (I didn't see this information otherwise.)I added two additional tests to cover existing functionality that needed to be verified with the changes in this PR.
The first is that the existing code allows explicitly named functions in the group list. I do not see this in the documentation and there were no tests for it. A decision can be made if this needs to be added to the documentation, or if it's a bug, but the test is there to make sure the behavior isn't accidentally changed.
The second is that method names that match groups names were being forced into those groups. For example, a method named
setters
would be forced into thesetters
group. I consider this a bug, and have corrected it in this PR. However, I'm willing to revert to existing behavior if it's deemed appropriate.