Skip to content

Commit 329c4c8

Browse files
committed
Add no-direct-mutation-state rule (fixes #133, #201)
1 parent 94ab26a commit 329c4c8

File tree

3 files changed

+152
-2
lines changed

3 files changed

+152
-2
lines changed

index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ module.exports = {
2727
'jsx-max-props-per-line': require('./lib/rules/jsx-max-props-per-line'),
2828
'jsx-no-literals': require('./lib/rules/jsx-no-literals'),
2929
'jsx-indent-props': require('./lib/rules/jsx-indent-props'),
30-
'jsx-closing-bracket-location': require('./lib/rules/jsx-closing-bracket-location')
30+
'jsx-closing-bracket-location': require('./lib/rules/jsx-closing-bracket-location'),
31+
'no-direct-mutation-state': require('./lib/rules/no-direct-mutation-state')
3132
},
3233
rulesConfig: {
3334
'jsx-uses-react': 0,
@@ -55,6 +56,7 @@ module.exports = {
5556
'jsx-max-props-per-line': 0,
5657
'jsx-no-literals': 0,
5758
'jsx-indent-props': 0,
58-
'jsx-closing-bracket-location': 0
59+
'jsx-closing-bracket-location': 0,
60+
'no-direct-mutation-state': 0
5961
}
6062
};

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* @fileoverview Prevent usage of setState in componentDidMount
3+
* @author David Petersen
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+
AssignmentExpression: function(node) {
20+
var item = node.left.object;
21+
while (item.object.property) {
22+
item = item.object;
23+
}
24+
if (
25+
item.object.type === 'ThisExpression' &&
26+
item.property.name === 'state'
27+
) {
28+
context.report(node.left.object, 'Do not mutate state directly. Use setState().');
29+
}
30+
}
31+
};
32+
33+
};
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/**
2+
* @fileoverview Prevent direct mutation of this.state
3+
* @author David Petersen
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
var rule = require('../../../lib/rules/no-direct-mutation-state');
12+
var RuleTester = require('eslint').RuleTester;
13+
14+
require('babel-eslint');
15+
16+
// ------------------------------------------------------------------------------
17+
// Tests
18+
// ------------------------------------------------------------------------------
19+
20+
var ruleTester = new RuleTester();
21+
ruleTester.run('no-direct-mutation-state', rule, {
22+
23+
valid: [{
24+
code: [
25+
'var Hello = React.createClass({',
26+
' render: function() {',
27+
' return <div>Hello {this.props.name}</div>;',
28+
' }',
29+
'});'
30+
].join('\n'),
31+
ecmaFeatures: {
32+
jsx: true
33+
}
34+
}, {
35+
code: [
36+
'var Hello = React.createClass({',
37+
' render: function() {',
38+
' var obj = {state: {}};',
39+
' obj.state.name = "foo";',
40+
' return <div>Hello {obj.state.name}</div>;',
41+
' }',
42+
'});'
43+
].join('\n'),
44+
ecmaFeatures: {
45+
jsx: true
46+
}
47+
}],
48+
49+
invalid: [{
50+
code: [
51+
'var Hello = React.createClass({',
52+
' render: function() {',
53+
' this.state.foo = "bar"',
54+
' return <div>Hello {this.props.name}</div>;',
55+
' }',
56+
'});'
57+
].join('\n'),
58+
ecmaFeatures: {
59+
jsx: true
60+
},
61+
errors: [{
62+
message: 'Do not mutate state directly. Use setState().'
63+
}]
64+
}, {
65+
code: [
66+
'var Hello = React.createClass({',
67+
' render: function() {',
68+
' this.state.person.name= "bar"',
69+
' return <div>Hello {this.props.name}</div>;',
70+
' }',
71+
'});'
72+
].join('\n'),
73+
ecmaFeatures: {
74+
jsx: true
75+
},
76+
errors: [{
77+
message: 'Do not mutate state directly. Use setState().'
78+
}]
79+
}, {
80+
code: [
81+
'var Hello = React.createClass({',
82+
' render: function() {',
83+
' this.state.person.name.first = "bar"',
84+
' return <div>Hello</div>;',
85+
' }',
86+
'});'
87+
].join('\n'),
88+
ecmaFeatures: {
89+
jsx: true
90+
},
91+
errors: [{
92+
message: 'Do not mutate state directly. Use setState().'
93+
}]
94+
}
95+
/**
96+
* Would be nice to prevent this too
97+
, {
98+
code: [
99+
'var Hello = React.createClass({',
100+
' render: function() {',
101+
' var that = this;',
102+
' that.state.person.name.first = "bar"',
103+
' return <div>Hello</div>;',
104+
' }',
105+
'});'
106+
].join('\n'),
107+
ecmaFeatures: {
108+
jsx: true
109+
},
110+
errors: [{
111+
message: 'Do not mutate state directly. Use setState().'
112+
}]
113+
}*/
114+
]
115+
});

0 commit comments

Comments
 (0)