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

[Core] Improve eslint a bit more #2903

Closed
17 of 20 tasks
oliviertassinari opened this issue Jan 12, 2016 · 39 comments
Closed
17 of 20 tasks

[Core] Improve eslint a bit more #2903

oliviertassinari opened this issue Jan 12, 2016 · 39 comments
Labels
umbrella For grouping multiple issues to provide a holistic view

Comments

@oliviertassinari
Copy link
Member

oliviertassinari commented Jan 12, 2016

Having a look at the source code, I think that we can stil improve it before starting doing heavy changes.
I was thinking about enforcing those rules:

@oliviertassinari oliviertassinari added Core umbrella For grouping multiple issues to provide a holistic view labels Jan 12, 2016
@alitaheri
Copy link
Member

Are these all that's left?

@oliviertassinari
Copy link
Member Author

I was also thinking about:

  • consistent-return: 2
  • no-shadow: 2
  • new-cap: [2, {capIsNew: true, newIsCap: true}]

But I'm not 100% convinced we need them.

@alitaheri
Copy link
Member

no-shadow is good to have. It can actually prevent some hideous bugs that can be caused by renaming inner function arguments. And I think there can be at most one or two occurrence of this

@oliviertassinari oliviertassinari mentioned this issue Jan 12, 2016
8 tasks
@alitaheri alitaheri added this to the 0.15.0 Release milestone Jan 12, 2016
@newoga
Copy link
Contributor

newoga commented Jan 12, 2016

I think these are all mostly good. I could definitely argue for some of them and I can't really argue against any of them. I'm open to what you both think is best.

At some point I would be curious to do a comparison of our eslint feature set to React's or AirBnb's. For example, is our's looser or stricter? And if we happen to be really close to one, maybe we could adopt it. If we're not close but feel strongly about ours, then we can leave it be.

@newoga
Copy link
Contributor

newoga commented Jan 19, 2016

We should finish discussing our stance on object-shorthand at some point.

@newoga
Copy link
Contributor

newoga commented Jan 26, 2016

I think we should add jsx-no-bind when we're able to: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md

Should be pretty simple:

src/auto-complete.jsx
  408:13  error  JSX props should not use arrow functions  react/jsx-no-bind
  414:13  error  JSX props should not use arrow functions  react/jsx-no-bind
  418:13  error  JSX props should not use arrow functions  react/jsx-no-bind
  422:13  error  JSX props should not use arrow functions  react/jsx-no-bind

src/DropDownMenu/DropDownMenu.jsx
  388:11  error  JSX props should not use .bind()  react/jsx-no-bind

src/time-picker/clock.jsx
  180:11  error  JSX props should not use .bind()  react/jsx-no-bind
  181:11  error  JSX props should not use .bind()  react/jsx-no-bind

src/time-picker/time-display.jsx
  151:11  error  JSX props should not use arrow functions  react/jsx-no-bind
  158:11  error  JSX props should not use arrow functions  react/jsx-no-bind

@mbrookes
Copy link
Member

@newoga out of interest, what would you use instead of:

onTouchTap={() => this.props.onSelectAffix('pm')}

@newoga
Copy link
Contributor

newoga commented Jan 26, 2016

@mbrookes This is less of a style preference and more for performance! I didn't realize it until just recently when I read a blog post. I'm not quite the best approach of how to handle this all the time, but this would be better I think:

handleOnTouchTap(value) {
  this.props.onSelectAffix(value);
}

<Component prop={this.handleOntouchTap('pm')} />

@newoga
Copy link
Contributor

newoga commented Jan 26, 2016

Note, our performance issues related in the <DropDownMenu /> component might be related to this jsx-bind. (#2787). Our previous implementation in 0.13.x didn't using jsx binding but out current one in 0.14.x uses it on looped <MenuItem />. It'd be interesting to see if that fixes it.

@newoga
Copy link
Contributor

newoga commented Jan 26, 2016

The jsx-no-bind doc says this:

A bind call or arrow function in a JSX prop will create a brand new function on every single render. This is bad for performance, as it will result in the garbage collector being invoked way more than is necessary.

@oliviertassinari
Copy link
Member Author

jsx-no-bind is definitely a performance linting rule. I was considering it on my project some time ago.
The thing is, it was too much code refactorization. In order to work. You almost always have to create a new component.
But now, we have stateless functional component. That point shouldn't be an issue anymore.
I think that it's a good idea to enforce this rule 👍.

but this would be better I think:

@newoga I think that your proposal doesn't solve anything 😁.
This should be better:

handleOnTouchTap(value) {
  this.props.onSelectAffix(value);
}

<Component prop={this.handleOntouchTap} type="pm" />

@newoga
Copy link
Contributor

newoga commented Jan 26, 2016

Haha, I knew something didn't look right when I typed it 😁! I guess the component that is calling the handleOnTouchTap would just have to pass type=pm back.

@newoga
Copy link
Contributor

newoga commented Jan 26, 2016

@oliviertassinari Maybe for DropDownMenu we should make MenuItem also pass the key prop back if it was provided. That way we could get rid of the .bind() in DropDownMenu . In other words, we already have the new new component, we're just not passing back the data we need.

@mbrookes
Copy link
Member

@oliviertassinari No, this should be better. 😏 :

handleOnTouchTap() {
  this.props.onSelectAffix();
}

<Component prop={this.handleOntouchTap} type=“pm" />

@newoga thanks for the explanation, I wasn't aware of the performance implication - I'll keep it in mind for my own code.

@alitaheri
Copy link
Member

@oliviertassinari That will still create a new element object. I don't think jsx-no-bind really does solve any performance issues. the only think I can think of is pure-rendering. if the same function is always passed down then pure-rendering can be implemented, otherwise it won't work. but with memory allocation jsx-no-bind doesn't solve a thing in this one case (with pure functions it does, but not with ones requiring a closure as mentioned by @mbrookes ).

I think this low level of optimization should be avoided as it complicates the code. If we properly implement pure-render (Recompose pure or onlyUpdateForKeys) we really shouldn't worry about a bunch of functions being created and destroyed. But I like the no-bind with allow-arrow, it's a lot prettier style 😁

@alitaheri
Copy link
Member

@mbrookes @oliviertassinari Nope:

<Component prop={this.props.onSelectAffix} type=“pm"/>

@oliviertassinari
Copy link
Member Author

@alitaheri Good point. We save one function allocation but we then have the memory allocation required for a component. Not that great 😁. I'm wondering if we can solve the binding without introducing a new component in some cases.

@mbrookes
Copy link
Member

@alitaheri - ah, you win! 😆

@alitaheri
Copy link
Member

@oliviertassinari Functions are immutable. you can't change what they have in their closures. in some cases it's simply impossible. I'd say we really shouldn't worry about this. I mean if we memoize style objects all our problems will be solved. and these functions will go unnoticed for as long as javascript lives 😆 There are much bigger fish to fry than this. let's cook'em first and then if profilers brag about these function allocations we'll come back to this issue 😁

@mbrookes :bowtie:

@newoga
Copy link
Contributor

newoga commented Jan 26, 2016

We save one function allocation but we then have the memory allocation required for a component. Not that great 😁.

I don't think that's correct, because we're looping through menu items:

const menuItemElements = menuItems
      ? menuItems.map((item, idx) => (
        <MenuItem
          key={idx}
          primaryText={item[displayMember || 'text']}
          value={item[valueMember]}
          onTouchTap={this._onMenuItemTouchTap.bind(this, idx, item)}
        />
      ))
      : React.Children.map(children, child => {
        const clone = React.cloneElement(child, {
          onTouchTap: this._onMenuItemTouchTap.bind(this, index, child.props.value),
        }, child.props.children);
        index += 1;
        return clone;
      });

According to the docs, this would be a new function allocation per component that is rendered rather than just one for all?

@alitaheri
Copy link
Member

@newoga There is nothing we can do about it in this case since it's placing idx inside a closure. there MUST be a new function allocated for each element, or a simulated closure by an HOC, which will then have to allocate an element object for it. It's like the pigeon hole principle. you can't defy the fact that you need SOMETHING to hold that idx inside of it. it being a function closure, a mutable object (EXTREMELY dangerous), another element from an HOC, etc... you need to allocate something to hold a reference to where idx is in the computer memory 😁

@newoga
Copy link
Contributor

newoga commented Jan 26, 2016

@alitaheri Okay fair enough! 😄

@newoga
Copy link
Contributor

newoga commented Feb 8, 2016

I just updated my eslint-airbnb configuration in my personal project today and they started enforcing this:

http://eslint.org/docs/rules/no-unneeded-ternary

That might be a good one for us to add to the list.

@alitaheri This one is for you. 😆

@oliviertassinari
Copy link
Member Author

@newoga I'm loving it!

@alitaheri
Copy link
Member

Disallow Nested Ternaries (no-nested-ternary)

No more monsters within monsters 😆

@mbrookes
Copy link
Member

mbrookes commented Feb 8, 2016

There's nothing inherently wrong with nested ternaries, as long as they're formatted correctly.

This:

let thing = foo ? bar
  : (baz === qux) ? quxx
    : foobar;

is more readable than both of the following, and more compact than the latter:

let thing = foo ? bar : baz === qux ? quxx : foobar;
let thing;

if (foo) {
  thing = bar;
} else if (baz === qux) {
  thing = quxx;
} else {
  thing = foobar;
}

@Kosta-Github
Copy link
Contributor

If you format your last example like this:

let thing;
if      (foo)         { thing = bar; }
else if (baz === qux) { thing = quxx; }
else                  { thing = foobar; }

@alitaheri
Copy link
Member

There is indeed nothing wrong with well formatted nested ternary. I use it myself, to get away with singular expression:

const foo = cond1
  ? value1
  : cond2
    ? value2
    : value3;

The real issue is when value is expression itself...

const foo = criteria1 === stuff < 0
  ? [(stuff + bar), ...restOfTheStuff]
  : cond2
    ? [someOtherIrrelevantStuff]
    : {object: "?!!!"};

that begins to lose it's readability really fast. I have nothing against ternary, I even sometimes nest them 3 levels deep and it still remains readable.

But they should be used to make code easier to read, as should everything else! not make it harder. this applies to everything. even if/else chains. if it makes code readable use it, if it feels "clever", then it's wrong.

@oliviertassinari
Copy link
Member Author

If you format your last example like this:

IMHO, using space to align the code is a bad idea for maintainability.

@mbrookes
Copy link
Member

mbrookes commented Feb 8, 2016

I think we all agree then - use appropriately, format properly, and !no-nested-ternary. :)

@mbrookes
Copy link
Member

Would you consider adding this to the list? http://eslint.org/docs/rules/operator-linebreak

I noticed in Stepper that @namKolo put the + for string continuation linebreaks on the following line, which may be preferable, as extending a string onto a new line then only requires one line change, not two.

@oliviertassinari
Copy link
Member Author

That's a good point. I think that the best to do is to use operator-linebreak: [2, "after"] so that, when reading a line, if there is an operator at the end, you know there is more.

@mbrookes
Copy link
Member

Either works for me, but good to have the consistency.

@alitaheri
Copy link
Member

I think we should add no-shadow to the list too.

@newoga
Copy link
Contributor

newoga commented Feb 28, 2016

I saw no-multiple-empty-lines in the reactjs eslint configuration and thought that could be good idea.

@mbrookes
Copy link
Member

Lets hold these until we get the bulk of the bugfix / enhancement PRs merged.

@newoga
Copy link
Contributor

newoga commented Feb 28, 2016

Lets hold these until we get the bulk of the bugfix / enhancement PRs merged.

I agree, let's do a freeze on eslinting for now until we're ready to do a lot of the big remaining pieces. When that's done we'll give @oliviertassinari a few days to perfect the rest of these 😁

@oliviertassinari
Copy link
Member Author

Alright, let's wait.
I'm gonna continue investigating react native (v20 is breaking it, dam it).

@oliviertassinari oliviertassinari modified the milestones: 0.16.0 Release, 0.15.0 Release Mar 6, 2016
@nathanmarks
Copy link
Member

@oliviertassinari

please please please react/no-multi-comp: 2 😄 The dialog.jsx file is driving me nuts.. lol

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
umbrella For grouping multiple issues to provide a holistic view
Projects
None yet
Development

No branches or pull requests

6 participants