Skip to content

Commit 55b9cbe

Browse files
committed
Add no-find-dom-node rule (fixes #678)
1 parent 30b267b commit 55b9cbe

File tree

5 files changed

+185
-1
lines changed

5 files changed

+185
-1
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#
8787
* [react/no-did-mount-set-state](docs/rules/no-did-mount-set-state.md): Prevent usage of `setState` in `componentDidMount`
8888
* [react/no-did-update-set-state](docs/rules/no-did-update-set-state.md): Prevent usage of `setState` in `componentDidUpdate`
8989
* [react/no-direct-mutation-state](docs/rules/no-direct-mutation-state.md): Prevent direct mutation of `this.state`
90+
* [react/no-find-dom-node](docs/rules/no-find-dom-node.md): Prevent usage of `findDOMNode`
9091
* [react/no-is-mounted](docs/rules/no-is-mounted.md): Prevent usage of `isMounted`
9192
* [react/no-multi-comp](docs/rules/no-multi-comp.md): Prevent multiple component definition per file
9293
* [react/no-render-return-value](docs/rules/no-render-return-value.md): Prevent usage of the return value of `React.render`
@@ -162,6 +163,7 @@ The rules enabled in this configuration are:
162163
* [react/no-did-mount-set-state](docs/rules/no-did-mount-set-state.md) with `allow-in-func` option
163164
* [react/no-did-update-set-state](docs/rules/no-did-update-set-state.md) with `allow-in-func` option
164165
* [react/no-direct-mutation-state](docs/rules/no-direct-mutation-state.md)
166+
* [react/no-find-dom-node](docs/rules/no-find-dom-node.md)
165167
* [react/no-is-mounted](docs/rules/no-is-mounted.md)
166168
* [react/no-unknown-property](docs/rules/no-unknown-property.md)
167169
* [react/prop-types](docs/rules/prop-types.md)

docs/rules/no-find-dom-node.md

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Prevent usage of findDOMNode (no-find-dom-node)
2+
3+
Facebook will eventually deprecate `findDOMNode` as it blocks certain improvements in React in the future.
4+
5+
It is recommended to use callback refs instead. See [Dan Abramov comments and examples](https://github.com/yannickcr/eslint-plugin-react/issues/678#issue-165177220).
6+
7+
## Rule Details
8+
9+
The following patterns are considered warnings:
10+
11+
```js
12+
class MyComponent extends Component {
13+
componentDidMount() {
14+
findDOMNode(this).scrollIntoView();
15+
}
16+
render() {
17+
return <div />
18+
}
19+
}
20+
```
21+
22+
The following patterns are not considered warnings:
23+
24+
```js
25+
class MyComponent extends Component {
26+
componentDidMount() {
27+
this.node.scrollIntoView();
28+
}
29+
render() {
30+
return <div ref={node => this.node = node} />
31+
}
32+
}
33+
```

index.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ module.exports = {
4747
'jsx-first-prop-new-line': require('./lib/rules/jsx-first-prop-new-line'),
4848
'jsx-no-target-blank': require('./lib/rules/jsx-no-target-blank'),
4949
'jsx-filename-extension': require('./lib/rules/jsx-filename-extension'),
50-
'require-optimization': require('./lib/rules/require-optimization')
50+
'require-optimization': require('./lib/rules/require-optimization'),
51+
'no-find-dom-node': require('./lib/rules/no-find-dom-node')
5152
},
5253
configs: {
5354
recommended: {
@@ -67,6 +68,7 @@ module.exports = {
6768
'react/no-did-mount-set-state': [2, 'allow-in-func'],
6869
'react/no-did-update-set-state': [2, 'allow-in-func'],
6970
'react/no-direct-mutation-state': 2,
71+
'react/no-find-dom-node': 2,
7072
'react/no-is-mounted': 2,
7173
'react/no-unknown-property': 2,
7274
'react/no-render-return-value': 2,

lib/rules/no-find-dom-node.js

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* @fileoverview Prevent usage of findDOMNode
3+
* @author Yannick Croissant
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 (
22+
callee.type !== 'MemberExpression' || (
23+
callee.object.callee && callee.object.callee.name !== 'findDOMNode' &&
24+
callee.property.name !== 'findDOMNode'
25+
)
26+
) {
27+
return;
28+
}
29+
var ancestors = context.getAncestors(callee);
30+
for (var i = 0, j = ancestors.length; i < j; i++) {
31+
if (ancestors[i].type === 'Property' || ancestors[i].type === 'MethodDefinition') {
32+
context.report({
33+
node: callee,
34+
message: 'Do not use findDOMNode'
35+
});
36+
break;
37+
}
38+
}
39+
}
40+
};
41+
42+
};
43+
44+
module.exports.schema = [];

tests/lib/rules/no-find-dom-node.js

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/**
2+
* @fileoverview Prevent usage of findDOMNode
3+
* @author Yannick Croissant
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
var rule = require('../../../lib/rules/no-find-dom-node');
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-find-dom-node', rule, {
27+
28+
valid: [{
29+
code: [
30+
'var Hello = function() {};'
31+
].join('\n'),
32+
parserOptions: parserOptions
33+
}, {
34+
code: [
35+
'var Hello = React.createClass({',
36+
' render: function() {',
37+
' return <div>Hello</div>;',
38+
' }',
39+
'});'
40+
].join('\n'),
41+
parserOptions: parserOptions
42+
}, {
43+
code: [
44+
'var Hello = React.createClass({',
45+
' componentDidMount: function() {',
46+
' someNonMemberFunction(arg);',
47+
' this.someFunc = React.findDOMNode;',
48+
' },',
49+
' render: function() {',
50+
' return <div>Hello</div>;',
51+
' }',
52+
'});'
53+
].join('\n'),
54+
parserOptions: parserOptions
55+
}],
56+
57+
invalid: [{
58+
code: [
59+
'var Hello = React.createClass({',
60+
' componentDidMount: function() {',
61+
' React.findDOMNode(this).scrollIntoView();',
62+
' },',
63+
' render: function() {',
64+
' return <div>Hello</div>;',
65+
' }',
66+
'});'
67+
].join('\n'),
68+
parserOptions: parserOptions,
69+
errors: [{
70+
message: 'Do not use findDOMNode'
71+
}]
72+
}, {
73+
code: [
74+
'var Hello = React.createClass({',
75+
' componentDidMount: function() {',
76+
' ReactDOM.findDOMNode(this).scrollIntoView();',
77+
' },',
78+
' render: function() {',
79+
' return <div>Hello</div>;',
80+
' }',
81+
'});'
82+
].join('\n'),
83+
parserOptions: parserOptions,
84+
errors: [{
85+
message: 'Do not use findDOMNode'
86+
}]
87+
}, {
88+
code: [
89+
'class Hello extends Component {',
90+
' componentDidMount() {',
91+
' findDOMNode(this).scrollIntoView();',
92+
' }',
93+
' render() {',
94+
' return <div>Hello</div>;',
95+
' }',
96+
'};'
97+
].join('\n'),
98+
parserOptions: parserOptions,
99+
errors: [{
100+
message: 'Do not use findDOMNode'
101+
}]
102+
}]
103+
});

0 commit comments

Comments
 (0)