Skip to content
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

[destructuring-assignment] : new rule #1462

Merged
merged 5 commits into from Oct 27, 2017
Merged

Conversation

DianaSuvorova
Copy link
Contributor

@DianaSuvorova DianaSuvorova commented Oct 3, 2017

to enforce consistent usage of destructuring assignment closes #216, closes #556, closes #278

@DianaSuvorova DianaSuvorova changed the title feat[destructuring-props-argument] : new rule [destructuring-props-argument] : new rule Oct 3, 2017

## Rule Details

By default rule is set to `always` enforce destructuring assignment. The following pattern is considered warning:
Copy link
Member

Choose a reason for hiding this comment

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

presumably we'd want the default to be "always" on all component types; releasing the rule with only SFC support means we couldn't default class and createClass components to "always" without a major release

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 am not sure about your major release concern. Would releasing only SFC support require a major release? What's the problem eith having to do a major release?

Irrespective of that I have only one use case for "other" component types as per issue #278 and I am not particularly keen about it. Are you? Maybe it would make sense to have this rule for SFC only?

Copy link
Member

Choose a reason for hiding this comment

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

releasing only SFC support would be a minor.

Later releasing class-based component supported, defaulted to "ignore", would also be a minor.

However, later releasing class-based component supported, defaulted to "always", would be a major.

There's not a problem per se about releasing a major release, but it's always best to minimize breaking changes.

My use case for non-SFC components is that I want all this.props, this.state, and this.context properties destructured at the top of every function that uses them (that also includes context in SFCs).

It would be great if one rule could handle all of these cases - if not, I'd expect this rule to only ever handle SFCs (props and context), and a new rule to be added for "inside non-SFC functions".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, well in this case I can try to add the rest of the logic to this pr.
So both of the component types will enforce context and props. Class component will in addition enforce state.
Config structure would be: { SFC: always/never/ignore, class: always/never/ignore} (I don't like name class is there a better term?)
default: { SFC: always, class: always }

there is also a nextProps, prevProps which I would ignore for now.
Does this make sense?

Copy link
Member

Choose a reason for hiding this comment

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

i think class is probably the best name, unfortunately.

I agree ignoring next/prev props is fine for now, in SFCs, it'd be props and context, and in class-based and createClass components, it'd be this.props and this.state and this.context.

if (relatedComponent && isStatelessFunctionUsage && options.SFC === 'always') {
context.report({
node: node,
message: 'Should use destructuring props assignment in SFC argument'
Copy link
Member

Choose a reason for hiding this comment

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

s/Should/Must/g?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hm, what do you mean?

Copy link
Member

Choose a reason for hiding this comment

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

it's not that you should use it, it's that you must because of the rule

@DianaSuvorova DianaSuvorova changed the title [destructuring-props-argument] : new rule [destructuring-assignment] : new rule Oct 4, 2017
@DianaSuvorova
Copy link
Contributor Author

@ljharb , FYI, this is ready for review.

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

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

What about things like:

class Foo extends React.Component {
  render() { return this.foo(); }
  foo() {
    return this.props.children; // should be warned when "always"
  }
}

if (isPropUsed && options.SFC === 'always') {
context.report({
node: node,
message: `Should use destructuring ${node.object.name} assignment`
Copy link
Member

Choose a reason for hiding this comment

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

i think the word "must" here is better than "should"

if (classComponent) {
handleClassUsage(node, classComponent);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

can we also handle createClassComponents here?

@DianaSuvorova
Copy link
Contributor Author

DianaSuvorova commented Oct 6, 2017

@ljharb Ready for re-review

class Foo extends React.Component {
  render() { return this.foo(); }
  foo() {
    return this.props.children; // should be warned when "always"
  }
}
var Hello = React.createClass({
      render: function() {
        return <Text>{this.props.foo}</Text>;
      }
    });

Two above examples are added to test cases.

Changed should to must. I am not a big fan of must but I trust your judgement.

@ljharb
Copy link
Member

ljharb commented Oct 9, 2017

In specifications, "should" means the implementation can choose not to comply; "must" means they are forced to comply. If the rule is enabled, they're being forced to comply :-)

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

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

Can we ensure every example case has a test for both "default", "explicit always", and "explicit never"? In particular, the createClass ones.

I'm also wondering if class-based components and createClass components should have separate configurability?

@DianaSuvorova
Copy link
Contributor Author

DianaSuvorova commented Oct 10, 2017

I think that never setting doesn't really makes sense for class component. As it would flag any
const { foo } = this.props.
In SFC never is only enforced within the argument.

For example this test is invalid:

{
    code: `const MyComponent = ({ id, className }) => (
      <div id={id} className={className} />
    );`,
    options: [{SFC: 'never'}],
    errors: [
      {message: 'Must never use destructuring props assignment in SFC argument'}
    ]
  }

while this one is valid:

,{
    code: `const MyComponent = (props) => {
      const { id, className } = props;
      return <div id={id} className={className} />
    };`,
    options; [SFC: 'never']
  }

I'm also wondering if class-based components and createClass components should have separate configurability?

I would do it separately if it is a popular request.

I added explicit always test cases (which match default ones). And few more tests for class components. Hope it covers everything.

@DianaSuvorova
Copy link
Contributor Author

@ljharb , btw I am squashing any new commits I add. It may look the last commit was 8days ago but I just pushed the change.

@ljharb
Copy link
Member

ljharb commented Oct 11, 2017

I think that never setting doesn't really makes sense for class component. As it would flag any
const { foo } = this.props.

Yes, that's exactly what I'm expecting - if the user chooses "never", then they want this.props.foo to be used every time.

@DianaSuvorova
Copy link
Contributor Author

@ljharb, this is ready for review

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

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

This still needs an option and documentation for createClass components.

@@ -0,0 +1,87 @@
# Enforce consostent usage of destructuring assignment of props, state and context (react/destructuring-assignment)

Rule can be set to either of `always`, `never`, `ignore` for `class` and `SFC` components.
Copy link
Member

Choose a reason for hiding this comment

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

this should probably mention createClass components as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ljharb, do you suggest createClass as additional option? So it would be [SFC, createClass, class]? If so can it be done as a separate PR? Right now class option includes createClass.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I think they should be separate options - i think it'd be fine to add it in a separate PR, but I don't think this should be merged with class handling both :-/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmmm, could you explain rationale behind that? To me this rule almost can be monolithic (cover all possible react components with a single setting). I can see the need for SFC vs Class components distinction, but not class vs createClass. What is the use case?

Copy link
Member

Choose a reason for hiding this comment

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

For one, createClass components are often legacy, and legacy things need different rules. Mainly though, all three are actually distinct constructs - createClass components have different behavior than class-based components, in particular around auto-binding all the "instance" methods;

I think that it'd be fine to allow a string or an object option, where the string option is a single setting for all components - but the object option is intended to allow fine-grained control, so it's supposed to be not monolithic by design.

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 agree, technically if we are going to list component types, we should list them all. But even though it is very little code to be added to support the 3rd createClass option, I am not very comfortable adding a special config option for a deprecated functionality. Additionally the difference in behaviour between class and createClass component doesn't seem to be related to what this rule is checking (in both props are referenced with this.props right, or am I wrong?).

So seems like the single string option controlling all components is an acceptable solution for both of us and is common pattern across the library. I will update my pr shortly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ljharb I added an incremental change to switch to a single string option so it might be easier to review.


Rule can be set to either of `always`, `never`, `ignore` for `class` and `SFC` components.
```js
"react/destructuring-assignment": [<enabled>, { "SFC": "always", "class": "always"}]
Copy link
Member

Choose a reason for hiding this comment

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

also here

module.exports = {
meta: {
docs: {
description: 'Enforce consostent usage of destructuring assignment of props, state and context',
Copy link
Member

Choose a reason for hiding this comment

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

s/consostent/consistent

also, please use an Oxford comma after state

@hendrikswan
Copy link

@DianaSuvorova, thanks for creating this rule - my team is looking forward to when this gets merged in :)

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

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

lol, that's one way to settle the debate :-p

this is fine; we can add more granular options for the 3 component kinds later :-)

@@ -0,0 +1,87 @@
# Enforce consostent usage of destructuring assignment of props, state and context (react/destructuring-assignment)
Copy link
Member

Choose a reason for hiding this comment

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

consostent → consistent, and please use an Oxford comma after "state"

@DianaSuvorova
Copy link
Contributor Author

@ljharb anything else is missing for this PR?

@hendrikswan
Copy link

@DianaSuvorova it seems like the changes were approved?

@DianaSuvorova
Copy link
Contributor Author

@hendrikswan, yeah, but I don't have permissions to merge to master.

@hendrikswan
Copy link

hendrikswan commented Oct 24, 2017 via email

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