Repository: yannickcr/eslint-plugin-react
Pull request #250 (2015-10-15 16:05:10)

Rule to prefer es6 class instead of React createClass


Patch:
diff --git a/README.md b/README.md
index 4cb9ad26..45cb48e8 100644
--- a/README.md
+++ b/README.md
@@ -73,7 +73,8 @@ Finally, enable all of the rules that you would like to use.
     "react/require-extension": 1,
     "react/self-closing-comp": 1,
     "react/sort-comp": 1,
-    "react/wrap-multilines": 1
+    "react/wrap-multilines": 1,
+    "prefer-es6-class": 1,
   }
 }
 ```
@@ -108,6 +109,7 @@ Finally, enable all of the rules that you would like to use.
 * [self-closing-comp](docs/rules/self-closing-comp.md): Prevent extra closing tags for components without children
 * [sort-comp](docs/rules/sort-comp.md): Enforce component methods order
 * [wrap-multilines](docs/rules/wrap-multilines.md): Prevent missing parentheses around multilines JSX
+* [prefer-es6-class](docs/rules/prefer-es6-class.md): Prefer es6 class instead of createClass for React Components
 
 ## To Do
 
diff --git a/docs/rules/prefer-es6-class.md b/docs/rules/prefer-es6-class.md
new file mode 100644
index 00000000..9f3be1cb
--- /dev/null
+++ b/docs/rules/prefer-es6-class.md
@@ -0,0 +1,27 @@
+ES6 class bodies are more terse than traditional object literals. Methods do not require a `function` keyword and no commas are needed to separate them. This refactoring looks as such:
+
+Invalid:
+
+```js
+var ExampleComponent = React.createClass({
+ render: function() { 
+  return <div onClick={this._handleClick}>Hello, world.</div>;
+ },
+ _handleClick: function() {
+  console.log(this);
+ }
+});
+```
+
+Valid:
+
+```js
+class ExampleComponent extends React.Component {
+ render() { 
+  return <div onClick={this._handleClick}>Hello, world.</div>;
+ }
+ _handleClick() {
+  console.log(this);
+ }
+}
+```
\ No newline at end of file
diff --git a/index.js b/index.js
index 192dd60f..0a2ec93a 100644
--- a/index.js
+++ b/index.js
@@ -29,7 +29,8 @@ module.exports = {
     'jsx-indent-props': require('./lib/rules/jsx-indent-props'),
     'jsx-closing-bracket-location': require('./lib/rules/jsx-closing-bracket-location'),
     'no-direct-mutation-state': require('./lib/rules/no-direct-mutation-state'),
-    'forbid-prop-types': require('./lib/rules/forbid-prop-types')
+    'forbid-prop-types': require('./lib/rules/forbid-prop-types'),
+    'prefer-es6-class': require('./lib/rules/prefer-es6-class')
   },
   rulesConfig: {
     'jsx-uses-react': 0,
@@ -59,6 +60,7 @@ module.exports = {
     'jsx-indent-props': 0,
     'jsx-closing-bracket-location': 0,
     'no-direct-mutation-state': 0,
-    'forbid-prop-types': 0
+    'forbid-prop-types': 0,
+    'prefer-es6-class': 0
   }
 };
diff --git a/lib/rules/prefer-es6-class.js b/lib/rules/prefer-es6-class.js
new file mode 100644
index 00000000..7b7ffacf
--- /dev/null
+++ b/lib/rules/prefer-es6-class.js
@@ -0,0 +1,24 @@
+/**
+ * @fileoverview Prefer es6 class instead of createClass for React Component
+ * @author Dan Hamilton
+ */
+'use strict';
+
+var componentUtil = require('../util/component');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+  return {
+    ObjectExpression: function(node) {
+      if (componentUtil.isComponentDefinition(context, node)) {
+        context.report(node, 'Component should use es6 class instead of createClass');
+      }
+    }
+  };
+};
+
+module.exports.schema = [];


Test patch:
diff --git a/tests/lib/rules/prefer-es6-class.js b/tests/lib/rules/prefer-es6-class.js
new file mode 100644
index 00000000..a1902e3f
--- /dev/null
+++ b/tests/lib/rules/prefer-es6-class.js
@@ -0,0 +1,65 @@
+/**
+ * @fileoverview Prefer es6 class instead of createClass for React Component
+ * @author Dan Hamilton
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+var rule = require('../../../lib/rules/prefer-es6-class');
+var RuleTester = require('eslint').RuleTester;
+
+require('babel-eslint');
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+var ruleTester = new RuleTester();
+ruleTester.run('prefer-es6-class', rule, {
+
+  valid: [{
+    code: [
+      'class Hello extends React.Component {',
+      '  render() {',
+      '    return <div>Hello {this.props.name}</div>;',
+      '  }',
+      '}',
+      'Hello.displayName = \'Hello\''
+    ].join('\n'),
+    ecmaFeatures: {
+      classes: true,
+      jsx: true
+    }
+  }, {
+    code: [
+      'var Hello = "foo";',
+      'module.exports = {};'
+    ].join('\n'),
+    ecmaFeatures: {
+      jsx: true
+    }
+  }
+
+  ],
+
+  invalid: [{
+    code: [
+      'var Hello = React.createClass({',
+      '  displayName: \'Hello\',',
+      '  render: function() {',
+      '    return <div>Hello {this.props.name}</div>;',
+      '  }',
+      '});'
+    ].join('\n'),
+    ecmaFeatures: {
+      classes: true,
+      jsx: true
+    },
+    errors: [{
+      message: 'Component should use es6 class instead of createClass'
+    }]
+  }
+]});