Skip to content

Commit b42f681

Browse files
committed
Fix no-direct-mutation-state to report only in React components (fixes #229)
1 parent 00f716a commit b42f681

File tree

2 files changed

+91
-1
lines changed

2 files changed

+91
-1
lines changed

lib/rules/no-direct-mutation-state.js

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,41 @@
44
*/
55
'use strict';
66

7+
var componentUtil = require('../util/component');
8+
var ComponentList = componentUtil.List;
9+
710
// ------------------------------------------------------------------------------
811
// Rule Definition
912
// ------------------------------------------------------------------------------
1013

1114
module.exports = function(context) {
1215

16+
var componentList = new ComponentList();
17+
18+
/**
19+
* Checks if the component is valid
20+
* @param {Object} component The component to process
21+
* @returns {Boolean} True if the component is valid, false if not.
22+
*/
23+
function isValid(component) {
24+
var isNotReactComponent = Boolean(component && !component.isReactComponent);
25+
var doNotMutateSetState = Boolean(component && !component.mutateSetState);
26+
27+
return isNotReactComponent || doNotMutateSetState;
28+
}
29+
30+
/**
31+
* Reports undeclared proptypes for a given component
32+
* @param {Object} component The component to process
33+
*/
34+
function reportMutations(component) {
35+
var mutation;
36+
for (var i = 0, j = component.mutations.length; i < j; i++) {
37+
mutation = component.mutations[i];
38+
context.report(mutation, 'Do not mutate state directly. Use setState().');
39+
}
40+
}
41+
1342
// --------------------------------------------------------------------------
1443
// Public
1544
// --------------------------------------------------------------------------
@@ -29,8 +58,33 @@ module.exports = function(context) {
2958
item.object.type === 'ThisExpression' &&
3059
item.property.name === 'state'
3160
) {
32-
context.report(node.left.object, 'Do not mutate state directly. Use setState().');
61+
var component = componentList.getByNode(context, node);
62+
var mutations = component && component.mutations || [];
63+
mutations.push(node.left.object);
64+
componentList.set(context, node, {
65+
mutateSetState: true,
66+
mutations: mutations
67+
});
68+
}
69+
},
70+
71+
'Program:exit': function() {
72+
var list = componentList.getList();
73+
for (var component in list) {
74+
if (!list.hasOwnProperty(component) || isValid(list[component])) {
75+
continue;
76+
}
77+
reportMutations(list[component]);
78+
}
79+
},
80+
81+
ReturnStatement: function(node) {
82+
if (!componentUtil.isReactComponent(context, node)) {
83+
return;
3384
}
85+
componentList.set(context, node, {
86+
isReactComponent: true
87+
});
3488
}
3589
};
3690

tests/lib/rules/no-direct-mutation-state.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,20 @@ ruleTester.run('no-direct-mutation-state', rule, {
5252
ecmaFeatures: {
5353
jsx: true
5454
}
55+
}, {
56+
code: [
57+
'class Hello {',
58+
' getFoo() {',
59+
' this.state.foo = \'bar\'',
60+
' return this.state.foo;',
61+
' }',
62+
'}'
63+
].join('\n'),
64+
ecmaFeatures: {
65+
classes: true,
66+
modules: true,
67+
jsx: true
68+
}
5569
}],
5670

5771
invalid: [{
@@ -99,6 +113,28 @@ ruleTester.run('no-direct-mutation-state', rule, {
99113
errors: [{
100114
message: 'Do not mutate state directly. Use setState().'
101115
}]
116+
}, {
117+
code: [
118+
'var Hello = React.createClass({',
119+
' render: function() {',
120+
' this.state.person.name.first = "bar"',
121+
' this.state.person.name.last = "baz"',
122+
' return <div>Hello</div>;',
123+
' }',
124+
'});'
125+
].join('\n'),
126+
ecmaFeatures: {
127+
jsx: true
128+
},
129+
errors: [{
130+
message: 'Do not mutate state directly. Use setState().',
131+
line: 3,
132+
column: 5
133+
}, {
134+
message: 'Do not mutate state directly. Use setState().',
135+
line: 4,
136+
column: 5
137+
}]
102138
}
103139
/**
104140
* Would be nice to prevent this too

0 commit comments

Comments
 (0)