diff --git a/README.md b/README.md
index 48acbcadfd..f0939fab2d 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,7 @@ Finally, enable all of the rules that you would like to use.
"react/jsx-max-props-per-line": 1,
"react/jsx-no-bind": 1,
"react/jsx-no-duplicate-props": 1,
+ "react/jsx-no-is-mounted": 1,
"react/jsx-no-literals": 1,
"react/jsx-no-undef": 1,
"react/jsx-pascal-case": 1,
@@ -110,6 +111,7 @@ Finally, enable all of the rules that you would like to use.
* [no-did-mount-set-state](docs/rules/no-did-mount-set-state.md): Prevent usage of `setState` in `componentDidMount`
* [no-did-update-set-state](docs/rules/no-did-update-set-state.md): Prevent usage of `setState` in `componentDidUpdate`
* [no-direct-mutation-state](docs/rules/no-direct-mutation-state.md): Prevent direct mutation of `this.state`
+* [no-is-mounted](docs/rules/no-is-mounted.md): Prevent usage of `isMounted`
* [no-multi-comp](docs/rules/no-multi-comp.md): Prevent multiple component definition per file
* [no-set-state](docs/rules/no-set-state.md): Prevent usage of `setState`
* [no-unknown-property](docs/rules/no-unknown-property.md): Prevent usage of unknown DOM property
diff --git a/docs/rules/no-is-mounted.md b/docs/rules/no-is-mounted.md
new file mode 100644
index 0000000000..365fc44167
--- /dev/null
+++ b/docs/rules/no-is-mounted.md
@@ -0,0 +1,34 @@
+# Prevent usage of isMounted (no-is-mounted)
+
+[`isMounted` is an anti-pattern](anti-pattern), is not available when using ES6 classes, and it is on its way to being officially deprecated.
+
+[anti-pattern]: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html
+
+## Rule Details
+
+The following patterns are considered warnings:
+
+```js
+var Hello = React.createClass({
+ handleClick: function() {
+ setTimeout(function() {
+ if (this.isMounted()) {
+ return;
+ }
+ });
+ },
+ render: function() {
+ return
Hello
;
+ }
+});
+```
+
+The following patterns are not considered warnings:
+
+```js
+var Hello = React.createClass({
+ render: function() {
+ return Hello
;
+ }
+});
+```
diff --git a/index.js b/index.js
index 88a3d00299..e482777787 100644
--- a/index.js
+++ b/index.js
@@ -10,6 +10,7 @@ module.exports = {
'self-closing-comp': require('./lib/rules/self-closing-comp'),
'no-danger': require('./lib/rules/no-danger'),
'no-set-state': require('./lib/rules/no-set-state'),
+ 'no-is-mounted': require('./lib/rules/no-is-mounted'),
'no-deprecated': require('./lib/rules/no-deprecated'),
'no-did-mount-set-state': require('./lib/rules/no-did-mount-set-state'),
'no-did-update-set-state': require('./lib/rules/no-did-update-set-state'),
@@ -47,6 +48,7 @@ module.exports = {
'no-deprecated': 0,
'no-danger': 0,
'no-set-state': 0,
+ 'no-is-mounted': 0,
'no-did-mount-set-state': 0,
'no-did-update-set-state': 0,
'react-in-jsx-scope': 0,
diff --git a/lib/rules/no-is-mounted.js b/lib/rules/no-is-mounted.js
new file mode 100644
index 0000000000..bfd8c1de7a
--- /dev/null
+++ b/lib/rules/no-is-mounted.js
@@ -0,0 +1,39 @@
+/**
+ * @fileoverview Prevent usage of isMounted
+ * @author Joe Lencioni
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+
+ CallExpression: function(node) {
+ var callee = node.callee;
+ if (callee.type !== 'MemberExpression') {
+ return;
+ }
+ if (callee.object.type !== 'ThisExpression' || callee.property.name !== 'isMounted') {
+ return;
+ }
+ var ancestors = context.getAncestors(callee);
+ for (var i = 0, j = ancestors.length; i < j; i++) {
+ if (ancestors[i].type === 'Property' || ancestors[i].type === 'MethodDefinition') {
+ context.report(callee, 'Do not use isMounted');
+ break;
+ }
+ }
+ }
+ };
+
+};
+
+module.exports.schema = [];
diff --git a/tests/lib/rules/no-is-mounted.js b/tests/lib/rules/no-is-mounted.js
new file mode 100644
index 0000000000..9fd38fbcbe
--- /dev/null
+++ b/tests/lib/rules/no-is-mounted.js
@@ -0,0 +1,110 @@
+/**
+ * @fileoverview Prevent usage of isMounted
+ * @author Joe Lencioni
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+var rule = require('../../../lib/rules/no-is-mounted');
+var RuleTester = require('eslint').RuleTester;
+
+var parserOptions = {
+ ecmaVersion: 6,
+ ecmaFeatures: {
+ jsx: true
+ }
+};
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+var ruleTester = new RuleTester();
+ruleTester.run('no-is-mounted', rule, {
+
+ valid: [{
+ code: [
+ 'var Hello = function() {',
+ '};'
+ ].join('\n'),
+ parserOptions: parserOptions
+ }, {
+ code: [
+ 'var Hello = React.createClass({',
+ ' render: function() {',
+ ' return Hello
;',
+ ' }',
+ '});'
+ ].join('\n'),
+ parserOptions: parserOptions
+ }, {
+ code: [
+ 'var Hello = React.createClass({',
+ ' componentDidUpdate: function() {',
+ ' someNonMemberFunction(arg);',
+ ' this.someFunc = this.isMounted;',
+ ' },',
+ ' render: function() {',
+ ' return Hello
;',
+ ' }',
+ '});'
+ ].join('\n'),
+ parserOptions: parserOptions
+ }],
+
+ invalid: [{
+ code: [
+ 'var Hello = React.createClass({',
+ ' componentDidUpdate: function() {',
+ ' if (!this.isMounted()) {',
+ ' return;',
+ ' }',
+ ' },',
+ ' render: function() {',
+ ' return Hello
;',
+ ' }',
+ '});'
+ ].join('\n'),
+ parserOptions: parserOptions,
+ errors: [{
+ message: 'Do not use isMounted'
+ }]
+ }, {
+ code: [
+ 'var Hello = React.createClass({',
+ ' someMethod: function() {',
+ ' if (!this.isMounted()) {',
+ ' return;',
+ ' }',
+ ' },',
+ ' render: function() {',
+ ' return Hello
;',
+ ' }',
+ '});'
+ ].join('\n'),
+ parserOptions: parserOptions,
+ errors: [{
+ message: 'Do not use isMounted'
+ }]
+ }, {
+ code: [
+ 'class Hello extends React.Component {',
+ ' someMethod() {',
+ ' if (!this.isMounted()) {',
+ ' return;',
+ ' }',
+ ' }',
+ ' render() {',
+ ' return Hello
;',
+ ' }',
+ '};'
+ ].join('\n'),
+ parserOptions: parserOptions,
+ errors: [{
+ message: 'Do not use isMounted'
+ }]
+ }]
+});