Skip to content

Commit 7f81819

Browse files
author
Yannick Croissant
committed
Fix setState detection in arrow functions (fixes #931)
1 parent 13824c6 commit 7f81819

File tree

2 files changed

+77
-15
lines changed

2 files changed

+77
-15
lines changed

lib/rules/no-set-state.js

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*/
55
'use strict';
66

7+
var Components = require('../util/Components');
8+
79
// ------------------------------------------------------------------------------
810
// Rule Definition
911
// ------------------------------------------------------------------------------
@@ -18,7 +20,31 @@ module.exports = {
1820
schema: []
1921
},
2022

21-
create: function(context) {
23+
create: Components.detect(function(context, components, utils) {
24+
25+
/**
26+
* Checks if the component is valid
27+
* @param {Object} component The component to process
28+
* @returns {Boolean} True if the component is valid, false if not.
29+
*/
30+
function isValid(component) {
31+
return Boolean(component && !component.useSetState);
32+
}
33+
34+
/**
35+
* Reports usages of setState for a given component
36+
* @param {Object} component The component to process
37+
*/
38+
function reportSetStateUsages(component) {
39+
var setStateUsage;
40+
for (var i = 0, j = component.setStateUsages.length; i < j; i++) {
41+
setStateUsage = component.setStateUsages[i];
42+
context.report({
43+
node: setStateUsage,
44+
message: 'Do not use setState'
45+
});
46+
}
47+
}
2248

2349
// --------------------------------------------------------------------------
2450
// Public
@@ -28,24 +54,31 @@ module.exports = {
2854

2955
CallExpression: function(node) {
3056
var callee = node.callee;
31-
if (callee.type !== 'MemberExpression') {
57+
if (
58+
callee.type !== 'MemberExpression' ||
59+
callee.object.type !== 'ThisExpression' ||
60+
callee.property.name !== 'setState'
61+
) {
3262
return;
3363
}
34-
if (callee.object.type !== 'ThisExpression' || callee.property.name !== 'setState') {
35-
return;
36-
}
37-
var ancestors = context.getAncestors(callee);
38-
for (var i = 0, j = ancestors.length; i < j; i++) {
39-
if (ancestors[i].type === 'Property' || ancestors[i].type === 'MethodDefinition') {
40-
context.report({
41-
node: callee,
42-
message: 'Do not use setState'
43-
});
44-
break;
64+
var component = components.get(utils.getParentComponent());
65+
var setStateUsages = component && component.setStateUsages || [];
66+
setStateUsages.push(callee);
67+
components.set(node, {
68+
useSetState: true,
69+
setStateUsages: setStateUsages
70+
});
71+
},
72+
73+
'Program:exit': function() {
74+
var list = components.list();
75+
for (var component in list) {
76+
if (!list.hasOwnProperty(component) || isValid(list[component])) {
77+
continue;
4578
}
79+
reportSetStateUsages(list[component]);
4680
}
4781
}
4882
};
49-
50-
}
83+
})
5184
};

tests/lib/rules/no-set-state.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,5 +107,34 @@ ruleTester.run('no-set-state', rule, {
107107
errors: [{
108108
message: 'Do not use setState'
109109
}]
110+
}, {
111+
code: [
112+
'class Hello extends React.Component {',
113+
' someMethod = () => {',
114+
' this.setState({',
115+
' name: this.props.name.toUpperCase()',
116+
' });',
117+
' }',
118+
' render() {',
119+
' return <div onClick={this.someMethod.bind(this)}>Hello {this.state.name}</div>;',
120+
' }',
121+
'};'
122+
].join('\n'),
123+
parser: 'babel-eslint',
124+
errors: [{
125+
message: 'Do not use setState'
126+
}]
127+
}, {
128+
code: [
129+
'class Hello extends React.Component {',
130+
' render() {',
131+
' return <div onMouseEnter={() => this.setState({dropdownIndex: index})} />;',
132+
' }',
133+
'};'
134+
].join('\n'),
135+
parser: 'babel-eslint',
136+
errors: [{
137+
message: 'Do not use setState'
138+
}]
110139
}]
111140
});

0 commit comments

Comments
 (0)