Skip to content

Commit aafafc4

Browse files
committed
Handle UpdateExpressions in no-direct-mutation-state
This rule should also catch increment & decrement operators.
1 parent 162b92b commit aafafc4

File tree

2 files changed

+69
-10
lines changed

2 files changed

+69
-10
lines changed

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

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,36 @@ module.exports = {
4545
}
4646
}
4747

48+
/**
49+
* Walks throughs the MemberExpression to the top-most property.
50+
* @param {Object} node The node to process
51+
* @returns {Object} The outer-most MemberExpression
52+
*/
53+
function getOuterMemberExpression(node) {
54+
while (node.object && node.object.property) {
55+
node = node.object;
56+
}
57+
return node;
58+
}
59+
60+
/**
61+
* Determine if this MemberExpression is for `this.state`
62+
* @param {Object} node The node to process
63+
* @returns {Boolean}
64+
*/
65+
function isStateMemberExpression(node) {
66+
return node.object.type === 'ThisExpression' && node.property.name === 'state';
67+
}
68+
69+
/**
70+
* Determine if we should currently ignore assignments in this component.
71+
* @param {?Object} component The component to process
72+
* @returns {Boolean} True if we should skip assignment checks.
73+
*/
74+
function shouldIgnoreComponent(component) {
75+
return !component || (component.inConstructor && !component.inCallExpression);
76+
}
77+
4878
// --------------------------------------------------------------------------
4979
// Public
5080
// --------------------------------------------------------------------------
@@ -64,19 +94,12 @@ module.exports = {
6494
},
6595

6696
AssignmentExpression(node) {
67-
let item;
6897
const component = components.get(utils.getParentComponent());
69-
if (!component || (component.inConstructor && !component.inCallExpression) || !node.left || !node.left.object) {
98+
if (shouldIgnoreComponent(component) || !node.left || !node.left.object) {
7099
return;
71100
}
72-
item = node.left;
73-
while (item.object && item.object.property) {
74-
item = item.object;
75-
}
76-
if (
77-
item.object.type === 'ThisExpression' &&
78-
item.property.name === 'state'
79-
) {
101+
const item = getOuterMemberExpression(node.left);
102+
if (isStateMemberExpression(item)) {
80103
const mutations = (component && component.mutations) || [];
81104
mutations.push(node.left.object);
82105
components.set(node, {
@@ -86,6 +109,22 @@ module.exports = {
86109
}
87110
},
88111

112+
UpdateExpression(node) {
113+
const component = components.get(utils.getParentComponent());
114+
if (shouldIgnoreComponent(component) || node.argument.type !== 'MemberExpression') {
115+
return;
116+
}
117+
const item = getOuterMemberExpression(node.argument);
118+
if (isStateMemberExpression(item)) {
119+
const mutations = (component && component.mutations) || [];
120+
mutations.push(item);
121+
components.set(node, {
122+
mutateSetState: true,
123+
mutations
124+
});
125+
}
126+
},
127+
89128
'CallExpression:exit': function(node) {
90129
components.set(node, {
91130
inCallExpression: false

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ ruleTester.run('no-direct-mutation-state', rule, {
6969
' }',
7070
'}'
7171
].join('\n')
72+
}, {
73+
code: [
74+
'class Hello extends React.Component {',
75+
' constructor() {',
76+
' this.state.foo = 1;',
77+
' }',
78+
'}'
79+
].join('\n')
7280
}, {
7381
code: `
7482
class OneComponent extends Component {
@@ -97,6 +105,18 @@ ruleTester.run('no-direct-mutation-state', rule, {
97105
errors: [{
98106
message: 'Do not mutate state directly. Use setState().'
99107
}]
108+
}, {
109+
code: [
110+
'var Hello = createReactClass({',
111+
' render: function() {',
112+
' this.state.foo++;',
113+
' return <div>Hello {this.props.name}</div>;',
114+
' }',
115+
'});'
116+
].join('\n'),
117+
errors: [{
118+
message: 'Do not mutate state directly. Use setState().'
119+
}]
100120
}, {
101121
code: [
102122
'var Hello = createReactClass({',

0 commit comments

Comments
 (0)