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

sort-comp: Consider separating static fields & instance fields into separate groups #599

Closed
shangxiao opened this issue May 13, 2016 · 12 comments

Comments

@shangxiao
Copy link

Currently sort-comp asks me to place instance fields after methods in my class. It's conventional to place instance fields above the constructor, underneath static fields.

Possibly also consider allowing static fields be grouped separately from static methods.

@ljharb
Copy link
Member

ljharb commented May 13, 2016

I'd expect static fields and static methods to both be the last items in the class body, after everything instance-related is done - although I agree that instance fields could belong above the constructor.

@le0nik
Copy link

le0nik commented Jul 17, 2016

I tried to add this feature with this PR: #685.

Will gladly take suggestions and feedback on it.

@yannickcr
Copy link
Member

Also, to continue in this direction, it could be good to have separate groups for static-method, static-properties, instance-methods and instance-properties.

@le0nik
Copy link

le0nik commented Jul 17, 2016

@yannickcr I like it, but it raises a couple of questions:

  1. What would be considered instance-methods and static-methods? Would they be just arrow function properties or also regular functions? I'll illustrate with examples.

Statics fields:

class Hello extends React.Component {
  static displayName = 'Hello'; // static-properties
  static fetch() {} // static-methods
  static process = () => {} // static-methods ?
}

Instance fields:

class Hello extends React.Component {
  count = 0; // instance-properties
  handleClick = () => {} //  instance-methods
  renderSeciton() {} // instance-methods ?
}

I think it should be consistent for both static-methods and instance-methods methods, but then renderSection in the example above would be semantically incorrect, because it wouldn't be an instance method.

  1. Is it possible to combine methods and properties in configuration to allow users to mix them up by default?

Right now static-methods allows this:

class Hello extends React.Component {
  static prop1 = 'Hello';
  static fetch() {}
  static prop2 = 'Bye';
}

but configuration like this:

[
  'static-properties',
  'static-methods'
]

wouldn't allow this and users wouldn't be able to go back to the way it was before without applying codemod

Thoughts?

@ljharb
Copy link
Member

ljharb commented Jul 18, 2016

There's actually a difference between a class method and a function-valued property - handleClick = () => {} is not an instance method, it's an instance property that has a function on it.

@le0nik
Copy link

le0nik commented Jul 18, 2016

@ljharb I think that it's pretty much the same thing, because functions on the prototype can be considered a property too. It's obvious if you use ES5 syntax:

class Hello extends React.Component {
  renderSection: function() {
  }
}

In my opinion we need to determine what instance actually is. I thought of it as an object itself as opposed to it's prototype and wanted to separate instance functions(which are basically function-valued properties, like handleClick = () => {}) and instance properties which users usually use to store data. The reason for this is that the most common case for function-valued properties on an instance(like handleClick) is the binding to this value(usually for event handlers), so users don't usually replace or assign to those properties later on. So

instance-methods would be things like handleClick = () => {};
and
instance-properties would be things like count = 0;

Maybe there can also be a prototype-methods special keyword, that would allow users to determine how functions like handleClick = () => {} and renderSection() {} should be ordered.

What do you think?

@ljharb
Copy link
Member

ljharb commented Jul 18, 2016

The difference is that class methods are non-enumerable (properties are enumerable) and they have a [[HomeObject]], which means they're eligible to use super.

@le0nik
Copy link

le0nik commented Jul 18, 2016

This is a good point. Didn't look at it from this perspective.

For now I don't know how to move further with this. There are multiple approaches that could be taken.
Could someone else weigh in on it?

@jozanza
Copy link

jozanza commented Jul 20, 2016

This may be outside of the scope of this particular PR, but I would love to have more granular control over the ordering without the use of groups. Similar to how regular expressions may be used inside of the order property, it would be really nice to have a syntax that disambiguates static, prototype, instance props/methods. At the moment, this linting rule will not play nice with classes that give the same name to both a static and a prototype/instance property due to this ambiguity.

Perhaps something along these lines:

// .eslintrc
"react/sort-comp": [2, {
  "order": [
    "foo", // prototype method (default -- already supported)
    "/bar/", // prototype method regex (already supported)
    "static foo", // static initializer
    "static /bar/", // static initializer regex
    "foo=", // instance initializer
    "/bar/=" // instance initializer regex
  ]
}]

My apologies if this wasn't quite what was meant by "weighing in". Thoughts?

Sidenote: Another thought I've had is that this plugin has utility beyond React Component classes. There's not much keeping it from being generally useful for all ES6+ class declarations. If the ambiguities between static/prototype/instance are tackled, that only leaves a few more cases such as getter/setters and symbols. I'd be happy to create a new issue for discussion if this isn't the appropriate thread.

@ktaras
Copy link

ktaras commented Dec 6, 2017

Strange. It seems like instance-variables works only for state.
I specified following order:

      - static-methods
      - instance-variables
      - lifecycle
      - everything-else
      - render

When I declare

class CoolComponent extends Component {
  state = {};
  ...
}

it's OK, but when I add some custom property, e.g.:

class CoolComponent extends Component {
  state = {};
  someList = [];
  ...
}

it throws an error:

someList should be placed after componentWillUnmount

@ljharb
Copy link
Member

ljharb commented Dec 6, 2017

@ktaras thanks, could you file that as a separate issue?

michaelhogg added a commit to michaelhogg/eslint-plugin-react that referenced this issue Apr 21, 2018
Regarding this issue:

    jsx-eslint#599
    sort-comp: Consider separating static fields & instance fields into separate groups

and this PR, released in v7.6.0:

    jsx-eslint#1470
    Add instance-methods and instance-variables to sort-comp.

there's a bug in the implementation of the `instance-methods` group.

Class instance methods can be declared in two different ways:

    class Main extends React.Component {

        // MethodDefinition -> FunctionExpression
        foo() {}

        // ClassProperty -> ArrowFunctionExpression
        bar = () => {}

    }

Using the `babel-eslint` parser, if a class instance method is
declared as a `FunctionExpression`, then the parent AST node is
a `MethodDefinition`, but the `sort-comp` rule is expecting the
parent node to be a `ClassProperty`, which is wrong.
@michaelhogg
Copy link

FYI: discussion of this issue is continuing in #1774.

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

No branches or pull requests

7 participants