Repository: yannickcr/eslint-plugin-react (js)

Issue:
Detect "function-bind" new syntax in jsx-no-bind
Babel stage-0 implement the ["function-bind"](http://babeljs.io/blog/2015/05/14/function-bind) proposal.
This new syntax offers a `::` suffix operator to make autobind:

```javascript
import React, { Component, PropTypes } from 'react'

export default class Example extends Component {

  state = {
    value: 'hi'
  }

  onInputChange = () => {
    this.setState({
      value: this.refs.value.getValue()
    })
  }

  render () {
    return (
      <input
        type="text"
        ref="value"
        value={this.state.value}
        onChange={::this.onInputChange}/>
    )
  }
}
```

This syntax is handy for React applications, as an alternative to [class properties](https://github.com/jeffmo/es-class-fields-and-static-properties#part-1-class-instance-fields).

But unfortunately, it uses bind (see [babel's ouput](http://babeljs.io/repl/#?evaluate=false&presets=es2015%2Creact%2Cstage-0%2Cstage-2&experimental=true&loose=false&spec=false&playground=false&code=import%20React%2C%20{%20Component%2C%20PropTypes%20}%20from%20%27react%27%0A%0Aexport%20default%20class%20Example%20extends%20Component%20{%0A%0A%20%20state%20%3D%20{%0A%20%20%20%20value%3A%20%27hi%27%0A%20%20}%0A%0A%20%20onInputChange%20%3D%20%28%29%20%3D%3E%20{%0A%20%20%20%20this.setState%28{%0A%20%20%20%20%20%20value%3A%20this.refs.value.getValue%28%29%0A%20%20%20%20}%29%0A%20%20}%0A%0A%20%20render%20%28%29%20{%0A%20%20%20%20return%20%28%0A%20%20%20%20%20%20%3Cinput%0A%20%20%20%20%20%20%20%20type%3D%22text%22%0A%20%20%20%20%20%20%20%20ref%3D%22value%22%0A%20%20%20%20%20%20%20%20value%3D{this.state.value}%0A%20%20%20%20%20%20%20%20onChange%3D{%3A%3Athis.onInputChange}%2F%3E%0A%20%20%20%20%29%0A%20%20}%0A}%0A)) and lead to the same performance issues as fat arrows and bind explicit call.

Pull request description:
[new] - Throw error on stage-0 function-bind syntax.
Fixes #532 

[function-bind](http://babeljs.io/blog/2015/05/14/function-bind)

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cd4bf692..3bdef24a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file.
 This project adheres to [Semantic Versioning](http://semver.org/).
 This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com).
 
+## [5.2.2] - 2016-06-17
+### Fixed
+* Fix `jsx-no-bind` crash ([#641][])
+
+[5.2.2]: https://github.com/yannickcr/eslint-plugin-react/compare/v5.2.1...v5.2.2
+[#641]: https://github.com/yannickcr/eslint-plugin-react/issues/641
+
 ## [5.2.1] - 2016-06-17
 ### Fixed
 * Fix `jsx-pascal-case` for namespaced components ([#637][] @evcohen)
diff --git a/lib/rules/jsx-no-bind.js b/lib/rules/jsx-no-bind.js
index 5d951550..091c1a8f 100644
--- a/lib/rules/jsx-no-bind.js
+++ b/lib/rules/jsx-no-bind.js
@@ -19,8 +19,7 @@ module.exports = function(context) {
       var callee = node.callee;
       if (
         !configuration.allowBind &&
-        callee.type !== 'MemberExpression' ||
-        callee.property.name !== 'bind'
+        (callee.type !== 'MemberExpression' || callee.property.name !== 'bind')
       ) {
         return;
       }
@@ -64,6 +63,14 @@ module.exports = function(context) {
           node: node,
           message: 'JSX props should not use arrow functions'
         });
+      } else if (
+        !configuration.allowBind &&
+        valueNode.type === 'BindExpression'
+      ) {
+        context.report({
+          node: node,
+          message: 'JSX props should not use ::'
+        });
       }
     }
   };
diff --git a/lib/rules/prop-types.js b/lib/rules/prop-types.js
index 72da2f22..d12578c4 100644
--- a/lib/rules/prop-types.js
+++ b/lib/rules/prop-types.js
@@ -545,11 +545,14 @@ module.exports = Components.detect(function(context, components, utils) {
             node.id.properties[i].value.type === 'ObjectPattern'
           );
           // let {firstname} = props
-          var statelessDestructuring = node.init.name === 'props' && utils.getParentStatelessComponent();
+          var directDestructuring =
+            node.init.name === 'props' &&
+            (utils.getParentStatelessComponent() || inConstructor())
+          ;
 
           if (thisDestructuring) {
             properties = node.id.properties[i].value.properties;
-          } else if (statelessDestructuring) {
+          } else if (directDestructuring) {
             properties = node.id.properties;
           } else {
             continue;
@@ -784,9 +787,13 @@ module.exports = Components.detect(function(context, components, utils) {
       // let {props: {firstname}} = this
       var thisDestructuring = destructuring && node.init.type === 'ThisExpression';
       // let {firstname} = props
-      var statelessDestructuring = destructuring && node.init.name === 'props' && utils.getParentStatelessComponent();
+      var directDestructuring =
+        destructuring &&
+        node.init.name === 'props' &&
+        (utils.getParentStatelessComponent() || inConstructor())
+      ;
 
-      if (!thisDestructuring && !statelessDestructuring) {
+      if (!thisDestructuring && !directDestructuring) {
         return;
       }
       markPropTypesAsUsed(node);
diff --git a/package.json b/package.json
index 41b7efe7..bbe9b7cf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint-plugin-react",
-  "version": "5.2.1",
+  "version": "5.2.2",
   "author": "Yannick Croissant <yannick.croissant+npm@gmail.com>",
   "description": "React specific linting rules for ESLint",
   "main": "index.js",


Test patch:
diff --git a/tests/lib/rules/jsx-no-bind.js b/tests/lib/rules/jsx-no-bind.js
index f6e68637..5e81d168 100644
--- a/tests/lib/rules/jsx-no-bind.js
+++ b/tests/lib/rules/jsx-no-bind.js
@@ -58,6 +58,20 @@ ruleTester.run('jsx-no-bind', rule, {
       code: '<div onClick={() => alert("1337")}></div>',
       options: [{allowArrowFunctions: true}],
       parser: 'babel-eslint'
+    },
+
+    // Redux connect
+    {
+      code: [
+        'class Hello extends Component {',
+        '  render() {',
+        '    return <div>Hello</div>;',
+        '  }',
+        '}',
+        'export default connect()(Hello);'
+      ].join('\n'),
+      options: [{allowBind: true}],
+      parser: 'babel-eslint'
     }
   ],
 
@@ -128,6 +142,23 @@ ruleTester.run('jsx-no-bind', rule, {
       code: '<div ref={c => this._input = c}></div>',
       errors: [{message: 'JSX props should not use arrow functions'}],
       parser: 'babel-eslint'
+    },
+
+    // Bind expression
+    {
+      code: '<div foo={::this.onChange} />',
+      errors: [{message: 'JSX props should not use ::'}],
+      parser: 'babel-eslint'
+    },
+    {
+      code: '<div foo={foo.bar::baz} />',
+      errors: [{message: 'JSX props should not use ::'}],
+      parser: 'babel-eslint'
+    },
+    {
+      code: '<div foo={foo::bar} />',
+      errors: [{message: 'JSX props should not use ::'}],
+      parser: 'babel-eslint'
     }
   ]
 });
diff --git a/tests/lib/rules/prop-types.js b/tests/lib/rules/prop-types.js
index 9ae80505..b1b7f109 100644
--- a/tests/lib/rules/prop-types.js
+++ b/tests/lib/rules/prop-types.js
@@ -2120,6 +2120,25 @@ ruleTester.run('prop-types', rule, {
       errors: [{
         message: '\'lastname\' is missing in props validation'
       }]
+    }, {
+      code: [
+        'class Hello extends React.Component {',
+        '  constructor(props, context) {',
+        '    super(props, context)',
+        '    const firstname = props.firstname;',
+        '    const {lastname} = props;',
+        '    this.state = {',
+        '      firstname,',
+        '      lastname',
+        '    }',
+        '  }',
+        '}'
+      ].join('\n'),
+      parser: 'babel-eslint',
+      errors: [
+        {message: '\'firstname\' is missing in props validation'},
+        {message: '\'lastname\' is missing in props validation'}
+      ]
     }
   ]
 });