-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f1e86b5
commit 63eaf60
Showing
5 changed files
with
257 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Prevent mutation of this.props (no-mutation-props) | ||
|
||
NEVER mutate `this.props`, as all React components must act like pure functions with respect to their props. | ||
Treat `this.props` as if it were immutable. More info available at [https://facebook.github.io/react/docs/components-and-props.html#props-are-read-only](https://facebook.github.io/react/docs/components-and-props.html#props-are-read-only) | ||
|
||
## Rule Details | ||
|
||
This rule is aimed to forbid the use of mutating `this.props`. | ||
|
||
The following patterns are considered warnings: | ||
|
||
```jsx | ||
var Hello = React.createClass({ | ||
render: function() { | ||
this.props.name = this.props.name.toUpperCase(); | ||
return <div>Hello {this.props.name}</div>; | ||
} | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/** | ||
* @fileoverview Prevent direct mutation of this.props | ||
* @author Ian Schmitz | ||
*/ | ||
'use strict'; | ||
|
||
var has = require('has'); | ||
var Components = require('../util/Components'); | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Rule Definition | ||
// ------------------------------------------------------------------------------ | ||
|
||
module.exports = { | ||
meta: { | ||
docs: { | ||
description: 'Prevent direct mutation of this.props', | ||
category: 'Possible Errors', | ||
recommended: true | ||
} | ||
}, | ||
|
||
create: Components.detect(function (context, components, utils) { | ||
|
||
/** | ||
* Checks if the component is valid | ||
* @param {Object} component The component to process | ||
* @returns {Boolean} True if the component is valid, false if not. | ||
*/ | ||
function isValid(component) { | ||
return Boolean(component && !component.mutateProps); | ||
} | ||
|
||
/** | ||
* Reports this.props mutations for a given component | ||
* @param {Object} component The component to process | ||
*/ | ||
function reportMutations(component) { | ||
var mutation; | ||
for (var i = 0, j = component.mutations.length; i < j; i++) { | ||
mutation = component.mutations[i]; | ||
context.report({ | ||
node: mutation, | ||
message: 'Do not mutate props.' | ||
}); | ||
} | ||
} | ||
|
||
// -------------------------------------------------------------------------- | ||
// Public | ||
// -------------------------------------------------------------------------- | ||
|
||
return { | ||
AssignmentExpression: function (node) { | ||
var item; | ||
|
||
if (!node.left || !node.left.object || !node.left.object.object) { | ||
return; | ||
} | ||
|
||
item = node.left.object; | ||
while (item.object.property) { | ||
item = item.object; | ||
} | ||
|
||
if (item.object.type === 'ThisExpression' && item.property.name === 'props') { | ||
var component = components.get(utils.getParentComponent()); | ||
var mutations = component && component.mutations || []; | ||
mutations.push(node.left.object); | ||
components.set(node, { | ||
mutateProps: true, | ||
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]); | ||
} | ||
} | ||
}; | ||
|
||
}) | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
/** | ||
* @fileoverview Prevent mutation of this.props | ||
* @author Ian Schmitz | ||
*/ | ||
'use strict'; | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Requirements | ||
// ------------------------------------------------------------------------------ | ||
|
||
var rule = require('../../../lib/rules/no-mutation-props'); | ||
var RuleTester = require('eslint').RuleTester; | ||
|
||
var parserOptions = { | ||
ecmaVersion: 6, | ||
ecmaFeatures: { | ||
jsx: true | ||
} | ||
}; | ||
|
||
require('babel-eslint'); | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Tests | ||
// ------------------------------------------------------------------------------ | ||
|
||
var ruleTester = new RuleTester(); | ||
ruleTester.run('no-mutation-props', rule, { | ||
|
||
valid: [{ | ||
code: [ | ||
'var Hello = React.createClass({', | ||
' render: function() {', | ||
' return <div>Hello {this.props.name}</div>;', | ||
' }', | ||
'});' | ||
].join('\n'), | ||
parserOptions: parserOptions | ||
}, { | ||
code: [ | ||
'var Hello = React.createClass({', | ||
' render: function() {', | ||
' var obj = {props: {}};', | ||
' obj.props.name = "foo";', | ||
' return <div>Hello {obj.props.name}</div>;', | ||
' }', | ||
'});' | ||
].join('\n'), | ||
parserOptions: parserOptions | ||
}, { | ||
code: [ | ||
'var Hello = "foo";', | ||
'module.exports = {};' | ||
].join('\n'), | ||
parserOptions: parserOptions | ||
}, { | ||
code: [ | ||
'class Hello {', | ||
' getFoo() {', | ||
' this.props.foo = \'bar\'', | ||
' return this.props.foo;', | ||
' }', | ||
'}' | ||
].join('\n'), | ||
parserOptions: parserOptions | ||
}], | ||
|
||
invalid: [{ | ||
code: [ | ||
'var Hello = React.createClass({', | ||
' render: function() {', | ||
' this.props.foo = "bar"', | ||
' return <div>Hello {this.props.name}</div>;', | ||
' }', | ||
'});' | ||
].join('\n'), | ||
parserOptions: parserOptions, | ||
errors: [{ | ||
message: 'Do not mutate props.' | ||
}] | ||
}, { | ||
code: [ | ||
'var Hello = React.createClass({', | ||
' render: function() {', | ||
' this.props.person.name= "bar"', | ||
' return <div>Hello {this.props.name}</div>;', | ||
' }', | ||
'});' | ||
].join('\n'), | ||
parserOptions: parserOptions, | ||
errors: [{ | ||
message: 'Do not mutate props.' | ||
}] | ||
}, { | ||
code: [ | ||
'var Hello = React.createClass({', | ||
' render: function() {', | ||
' this.props.person.name.first = "bar"', | ||
' return <div>Hello</div>;', | ||
' }', | ||
'});' | ||
].join('\n'), | ||
parserOptions: parserOptions, | ||
errors: [{ | ||
message: 'Do not mutate props.' | ||
}] | ||
}, { | ||
code: [ | ||
'var Hello = React.createClass({', | ||
' render: function() {', | ||
' this.props.person.name.first = "bar"', | ||
' this.props.person.name.last = "baz"', | ||
' return <div>Hello</div>;', | ||
' }', | ||
'});' | ||
].join('\n'), | ||
parserOptions: parserOptions, | ||
errors: [{ | ||
message: 'Do not mutate props.', | ||
line: 3, | ||
column: 5 | ||
}, { | ||
message: 'Do not mutate props.', | ||
line: 4, | ||
column: 5 | ||
}] | ||
} | ||
/** | ||
* Would be nice to prevent this too | ||
, { | ||
code: [ | ||
'var Hello = React.createClass({', | ||
' render: function() {', | ||
' var that = this;', | ||
' that.props.person.name.first = "bar"', | ||
' return <div>Hello</div>;', | ||
' }', | ||
'});' | ||
].join('\n'), | ||
parserOptions: parserOptions, | ||
errors: [{ | ||
message: 'Do not mutate props.' | ||
}] | ||
}*/ | ||
] | ||
}); |