Skip to content

Commit

Permalink
Add no-is-mounted rule
Browse files Browse the repository at this point in the history
isMounted is an anti-pattern [0], is not available when using ES6
classes, and will eventually be officially deprecated. This commit adds
a rule that prevents the use of this method.

[0]: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html

Finishes jsx-eslint#37
  • Loading branch information
lencioni committed Dec 19, 2015
1 parent 77723c4 commit b5df783
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
34 changes: 34 additions & 0 deletions docs/rules/no-is-mounted.md
Original file line number Diff line number Diff line change
@@ -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 <div onClick={this.handleClick.bind(this)}>Hello</div>;
}
});
```

The following patterns are not considered warnings:

```js
var Hello = React.createClass({
render: function() {
return <div onClick={this.props.handleClick}>Hello</div>;
}
});
```
2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down Expand Up @@ -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,
Expand Down
39 changes: 39 additions & 0 deletions lib/rules/no-is-mounted.js
Original file line number Diff line number Diff line change
@@ -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 = [];
110 changes: 110 additions & 0 deletions tests/lib/rules/no-is-mounted.js
Original file line number Diff line number Diff line change
@@ -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 <div>Hello</div>;',
' }',
'});'
].join('\n'),
parserOptions: parserOptions
}, {
code: [
'var Hello = React.createClass({',
' componentDidUpdate: function() {',
' someNonMemberFunction(arg);',
' this.someFunc = this.isMounted;',
' },',
' render: function() {',
' return <div>Hello</div>;',
' }',
'});'
].join('\n'),
parserOptions: parserOptions
}],

invalid: [{
code: [
'var Hello = React.createClass({',
' componentDidUpdate: function() {',
' if (!this.isMounted()) {',
' return;',
' }',
' },',
' render: function() {',
' return <div>Hello</div>;',
' }',
'});'
].join('\n'),
parserOptions: parserOptions,
errors: [{
message: 'Do not use isMounted'
}]
}, {
code: [
'var Hello = React.createClass({',
' someMethod: function() {',
' if (!this.isMounted()) {',
' return;',
' }',
' },',
' render: function() {',
' return <div onClick={this.someMethod.bind(this)}>Hello</div>;',
' }',
'});'
].join('\n'),
parserOptions: parserOptions,
errors: [{
message: 'Do not use isMounted'
}]
}, {
code: [
'class Hello extends React.Component {',
' someMethod() {',
' if (!this.isMounted()) {',
' return;',
' }',
' }',
' render() {',
' return <div onClick={this.someMethod.bind(this)}>Hello</div>;',
' }',
'};'
].join('\n'),
parserOptions: parserOptions,
errors: [{
message: 'Do not use isMounted'
}]
}]
});

0 comments on commit b5df783

Please sign in to comment.