Skip to content

Commit

Permalink
Allow this.state assignment on constructor
Browse files Browse the repository at this point in the history
see #832
  • Loading branch information
BuraBure committed May 25, 2017
1 parent 7ebcd48 commit bbb3313
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 16 deletions.
14 changes: 13 additions & 1 deletion docs/rules/no-direct-mutation-state.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Prevent direct mutation of this.state (react/no-direct-mutation-state)

NEVER mutate `this.state` directly, as calling `setState()` afterwards may replace
Don't mutate `this.state` directly, as calling `setState()` afterwards may replace
the mutation you made. Treat `this.state` as if it were immutable.

The only place that it's acceptable to assign this.state is on a ES6 class component constructor

## Rule Details

This rule is aimed to forbid the use of mutating `this.state` directly.
Expand Down Expand Up @@ -34,4 +36,14 @@ var Hello = createReactClass({
return <div>Hello {this.state.name}</div>;
}
});

class Hello extends React.Component {
constructor(props) {
super(props)

this.state = {
foo: 'bar',
}
}
}
```
43 changes: 28 additions & 15 deletions lib/rules/no-direct-mutation-state.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/**
* @fileoverview Prevent direct mutation of this.state
* @author David Petersen
* @author Nicolas Fernandez <@burabure>
*/
'use strict';

var has = require('has');
var Components = require('../util/Components');

// ------------------------------------------------------------------------------
Expand Down Expand Up @@ -49,11 +49,18 @@ module.exports = {
// --------------------------------------------------------------------------
// Public
// --------------------------------------------------------------------------
var inConstructor = false;

return {
AssignmentExpression: function(node) {
var item;
if (!node.left || !node.left.object || !node.left.object.object) {
MethodDefinition(node) {
if (node.kind === 'constructor') {
inConstructor = true;
}
},

AssignmentExpression(node) {
let item;
if (inConstructor || !node.left || !node.left.object || !node.left.object.object) {
return;
}
item = node.left.object;
Expand All @@ -64,26 +71,32 @@ module.exports = {
item.object.type === 'ThisExpression' &&
item.property.name === 'state'
) {
var component = components.get(utils.getParentComponent());
var mutations = component && component.mutations || [];
const component = components.get(utils.getParentComponent());
const mutations = (component && component.mutations) || [];
mutations.push(node.left.object);
components.set(node, {
mutateSetState: true,
mutations: mutations
mutations
});
}
},

'Program:exit': function() {
var list = components.list();
for (var component in list) {
if (!has(list, component) || isValid(list[component])) {
continue;
}
reportMutations(list[component]);
'MethodDefinition:exit': function (node) {
if (node.kind === 'constructor') {
inConstructor = false;
}
},

'Program:exit': function () {
const list = components.list();

Object.keys(list).forEach((key) => {
if (isValid(list[key])) {
return;
}
reportMutations(list[key]);
});
}
};

})
};
8 changes: 8 additions & 0 deletions tests/lib/rules/no-direct-mutation-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ ruleTester.run('no-direct-mutation-state', rule, {
' }',
'}'
].join('\n')
}, {
code: [
'class Hello extends React.Component {',
' constructor() {',
' this.state.foo = "bar"',
' }',
'}'
].join('\n')
}],

invalid: [{
Expand Down

0 comments on commit bbb3313

Please sign in to comment.