Skip to content

Commit abfb98b

Browse files
committed
Merge pull request jsx-eslint#366 from lencioni/no-is-mounted
Add no-is-mounted rule (fixes jsx-eslint#37)
2 parents 77723c4 + b5df783 commit abfb98b

File tree

5 files changed

+187
-0
lines changed

5 files changed

+187
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ Finally, enable all of the rules that you would like to use.
5757
"react/jsx-max-props-per-line": 1,
5858
"react/jsx-no-bind": 1,
5959
"react/jsx-no-duplicate-props": 1,
60+
"react/jsx-no-is-mounted": 1,
6061
"react/jsx-no-literals": 1,
6162
"react/jsx-no-undef": 1,
6263
"react/jsx-pascal-case": 1,
@@ -110,6 +111,7 @@ Finally, enable all of the rules that you would like to use.
110111
* [no-did-mount-set-state](docs/rules/no-did-mount-set-state.md): Prevent usage of `setState` in `componentDidMount`
111112
* [no-did-update-set-state](docs/rules/no-did-update-set-state.md): Prevent usage of `setState` in `componentDidUpdate`
112113
* [no-direct-mutation-state](docs/rules/no-direct-mutation-state.md): Prevent direct mutation of `this.state`
114+
* [no-is-mounted](docs/rules/no-is-mounted.md): Prevent usage of `isMounted`
113115
* [no-multi-comp](docs/rules/no-multi-comp.md): Prevent multiple component definition per file
114116
* [no-set-state](docs/rules/no-set-state.md): Prevent usage of `setState`
115117
* [no-unknown-property](docs/rules/no-unknown-property.md): Prevent usage of unknown DOM property

docs/rules/no-is-mounted.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Prevent usage of isMounted (no-is-mounted)
2+
3+
[`isMounted` is an anti-pattern](anti-pattern), is not available when using ES6 classes, and it is on its way to being officially deprecated.
4+
5+
[anti-pattern]: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html
6+
7+
## Rule Details
8+
9+
The following patterns are considered warnings:
10+
11+
```js
12+
var Hello = React.createClass({
13+
handleClick: function() {
14+
setTimeout(function() {
15+
if (this.isMounted()) {
16+
return;
17+
}
18+
});
19+
},
20+
render: function() {
21+
return <div onClick={this.handleClick.bind(this)}>Hello</div>;
22+
}
23+
});
24+
```
25+
26+
The following patterns are not considered warnings:
27+
28+
```js
29+
var Hello = React.createClass({
30+
render: function() {
31+
return <div onClick={this.props.handleClick}>Hello</div>;
32+
}
33+
});
34+
```

index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = {
1010
'self-closing-comp': require('./lib/rules/self-closing-comp'),
1111
'no-danger': require('./lib/rules/no-danger'),
1212
'no-set-state': require('./lib/rules/no-set-state'),
13+
'no-is-mounted': require('./lib/rules/no-is-mounted'),
1314
'no-deprecated': require('./lib/rules/no-deprecated'),
1415
'no-did-mount-set-state': require('./lib/rules/no-did-mount-set-state'),
1516
'no-did-update-set-state': require('./lib/rules/no-did-update-set-state'),
@@ -47,6 +48,7 @@ module.exports = {
4748
'no-deprecated': 0,
4849
'no-danger': 0,
4950
'no-set-state': 0,
51+
'no-is-mounted': 0,
5052
'no-did-mount-set-state': 0,
5153
'no-did-update-set-state': 0,
5254
'react-in-jsx-scope': 0,

lib/rules/no-is-mounted.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @fileoverview Prevent usage of isMounted
3+
* @author Joe Lencioni
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Rule Definition
9+
// ------------------------------------------------------------------------------
10+
11+
module.exports = function(context) {
12+
13+
// --------------------------------------------------------------------------
14+
// Public
15+
// --------------------------------------------------------------------------
16+
17+
return {
18+
19+
CallExpression: function(node) {
20+
var callee = node.callee;
21+
if (callee.type !== 'MemberExpression') {
22+
return;
23+
}
24+
if (callee.object.type !== 'ThisExpression' || callee.property.name !== 'isMounted') {
25+
return;
26+
}
27+
var ancestors = context.getAncestors(callee);
28+
for (var i = 0, j = ancestors.length; i < j; i++) {
29+
if (ancestors[i].type === 'Property' || ancestors[i].type === 'MethodDefinition') {
30+
context.report(callee, 'Do not use isMounted');
31+
break;
32+
}
33+
}
34+
}
35+
};
36+
37+
};
38+
39+
module.exports.schema = [];

tests/lib/rules/no-is-mounted.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* @fileoverview Prevent usage of isMounted
3+
* @author Joe Lencioni
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
var rule = require('../../../lib/rules/no-is-mounted');
12+
var RuleTester = require('eslint').RuleTester;
13+
14+
var parserOptions = {
15+
ecmaVersion: 6,
16+
ecmaFeatures: {
17+
jsx: true
18+
}
19+
};
20+
21+
// ------------------------------------------------------------------------------
22+
// Tests
23+
// ------------------------------------------------------------------------------
24+
25+
var ruleTester = new RuleTester();
26+
ruleTester.run('no-is-mounted', rule, {
27+
28+
valid: [{
29+
code: [
30+
'var Hello = function() {',
31+
'};'
32+
].join('\n'),
33+
parserOptions: parserOptions
34+
}, {
35+
code: [
36+
'var Hello = React.createClass({',
37+
' render: function() {',
38+
' return <div>Hello</div>;',
39+
' }',
40+
'});'
41+
].join('\n'),
42+
parserOptions: parserOptions
43+
}, {
44+
code: [
45+
'var Hello = React.createClass({',
46+
' componentDidUpdate: function() {',
47+
' someNonMemberFunction(arg);',
48+
' this.someFunc = this.isMounted;',
49+
' },',
50+
' render: function() {',
51+
' return <div>Hello</div>;',
52+
' }',
53+
'});'
54+
].join('\n'),
55+
parserOptions: parserOptions
56+
}],
57+
58+
invalid: [{
59+
code: [
60+
'var Hello = React.createClass({',
61+
' componentDidUpdate: function() {',
62+
' if (!this.isMounted()) {',
63+
' return;',
64+
' }',
65+
' },',
66+
' render: function() {',
67+
' return <div>Hello</div>;',
68+
' }',
69+
'});'
70+
].join('\n'),
71+
parserOptions: parserOptions,
72+
errors: [{
73+
message: 'Do not use isMounted'
74+
}]
75+
}, {
76+
code: [
77+
'var Hello = React.createClass({',
78+
' someMethod: function() {',
79+
' if (!this.isMounted()) {',
80+
' return;',
81+
' }',
82+
' },',
83+
' render: function() {',
84+
' return <div onClick={this.someMethod.bind(this)}>Hello</div>;',
85+
' }',
86+
'});'
87+
].join('\n'),
88+
parserOptions: parserOptions,
89+
errors: [{
90+
message: 'Do not use isMounted'
91+
}]
92+
}, {
93+
code: [
94+
'class Hello extends React.Component {',
95+
' someMethod() {',
96+
' if (!this.isMounted()) {',
97+
' return;',
98+
' }',
99+
' }',
100+
' render() {',
101+
' return <div onClick={this.someMethod.bind(this)}>Hello</div>;',
102+
' }',
103+
'};'
104+
].join('\n'),
105+
parserOptions: parserOptions,
106+
errors: [{
107+
message: 'Do not use isMounted'
108+
}]
109+
}]
110+
});

0 commit comments

Comments
 (0)