Skip to content

Commit 1230498

Browse files
committed
First commit to master
1 parent 72cece5 commit 1230498

File tree

6 files changed

+233
-0
lines changed

6 files changed

+233
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# eslint-plugin-react-functional-set-state
2+
3+
Lint Rule for React to use functional setState and prevent the use of accessing
4+
this.state and this.props inside of setState calls.
5+
6+
## Installation
7+
8+
You'll first need to install [ESLint](http://eslint.org):
9+
10+
```
11+
$ npm i eslint --save-dev
12+
```
13+
14+
Next, install `eslint-plugin-react-functional-set-state`:
15+
16+
```
17+
$ npm install eslint-plugin-react-functional-set-state --save-dev
18+
```
19+
20+
**Note:** If you installed ESLint globally (using the `-g` flag) then you must also install `eslint-plugin-react-functional-set-state` globally.
21+
22+
## Usage
23+
24+
Add `react-functional-set-state` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix:
25+
26+
```json
27+
{
28+
"plugins": [
29+
"react-functional-set-state"
30+
]
31+
}
32+
```
33+
34+
35+
Then configure the rules you want to use under the rules section.
36+
37+
```json
38+
{
39+
"rules": {
40+
"react-functional-set-state/no-this-state-props": 2
41+
}
42+
}
43+
```
44+
45+
## Supported Rules
46+
47+
* no-this-state-props

lib/index.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @fileoverview Lint Rule for React to use functional setState
3+
* @author Edwin Cromley
4+
*/
5+
"use strict";
6+
7+
//------------------------------------------------------------------------------
8+
// Requirements
9+
//------------------------------------------------------------------------------
10+
11+
var requireIndex = require("requireindex");
12+
13+
//------------------------------------------------------------------------------
14+
// Plugin Definition
15+
//------------------------------------------------------------------------------
16+
17+
18+
// import all rules in lib/rules
19+
module.exports.rules = {
20+
"no-this-state-props": require('./rules/no-this-state-props')
21+
}

lib/rules/no-this-state-props.js

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/**
2+
* @fileoverview Lint Rule for React to use functional setState
3+
* @author Edwin Cromley
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Rule Definition
9+
// ------------------------------------------------------------------------------
10+
11+
module.exports = {
12+
meta: {
13+
docs: {
14+
description: 'Prevent usage of this.state and this.props in setState',
15+
category: 'Stylistic Issues',
16+
recommended: false
17+
},
18+
schema: []
19+
},
20+
21+
create: function(context) {
22+
var setStateUsages = [];
23+
24+
function isThisSetState (node) {
25+
if ( node.type === 'CallExpression' ) {
26+
var callee = node.callee;
27+
var isMatching = (
28+
callee.type === 'MemberExpression' &&
29+
callee.object.type === 'ThisExpression' &&
30+
callee.property.name === 'setState'
31+
)
32+
return isMatching
33+
}
34+
}
35+
36+
function isThisState(node) {
37+
var isMatching = (
38+
node.type === 'MemberExpression' &&
39+
node.object.type === 'ThisExpression' &&
40+
node.property.name === 'state'
41+
)
42+
return isMatching
43+
}
44+
45+
function isThisProps(node) {
46+
var isMatching = (
47+
node.type === 'MemberExpression' &&
48+
node.object.type === 'ThisExpression' &&
49+
node.property.name === 'props'
50+
)
51+
return isMatching
52+
}
53+
54+
/**
55+
* Reports usages of setState where this.props or this.state are used.
56+
* @param {Object} setStateUsages Usages of set state.
57+
*/
58+
function reportSetStateUsages(setStateUsages) {
59+
var setStateUsage;
60+
for (var i = 0, j = setStateUsages.length; i < j; i++) {
61+
setStateUsage = setStateUsages[i];
62+
if ( isThisSetState(setStateUsage) ) {
63+
context.report({
64+
node: setStateUsage,
65+
message: 'Do not access this.state or this.props in setState(), instead apply a function with signature ( state, [props] ) to setState and access state and props.'
66+
});
67+
}
68+
}
69+
}
70+
71+
return {
72+
CallExpression: function(node) {
73+
var callee = node.callee;
74+
if ( ! isThisSetState(node) ) {
75+
setStateUsages.push(callee);
76+
}
77+
},
78+
79+
MemberExpression: function(node) {
80+
if ( isThisProps(node) || isThisState(node) ) {
81+
var ancestors = context.getAncestors()
82+
if ( ancestors.some(isThisSetState) ) {
83+
var setStateUsage = ancestors.find(isThisSetState)
84+
setStateUsages.push(setStateUsage);
85+
}
86+
}
87+
},
88+
89+
'Program:exit': function() {
90+
reportSetStateUsages(setStateUsages);
91+
}
92+
}
93+
}
94+
};

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
"name": "eslint-plugin-react-functional-set-state",
33
"version": "1.0.0",
44
"description": "Lint rule for React, should use functional setState",
5+
"repository": {
6+
"type": "git",
7+
"url": "https://github.com/npm/npm.git"
8+
},
59
"keywords": [
610
"eslint",
711
"eslintplugin",

tests/lib/rules/index.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* @fileoverview Tests for no-with rule.
3+
* @author Nicholas C. Zakas
4+
*/
5+
6+
"use strict";
7+
8+
//------------------------------------------------------------------------------
9+
// Requirements
10+
//------------------------------------------------------------------------------
11+
12+
var rule = require("../../../lib/rules/no-this-state-props");
13+
var RuleTester = require('eslint').RuleTester;
14+
15+
//------------------------------------------------------------------------------
16+
// Tests
17+
//------------------------------------------------------------------------------
18+
19+
var ruleTester = new RuleTester();
20+
ruleTester.run("no-this-state-props", rule, {
21+
valid: [
22+
{
23+
code: "this.setState( { opened: yolo } )",
24+
parserOptions: { ecmaVersion: 6 }
25+
},
26+
{
27+
code: "this.setState( ( state ) => ( { opened: ! state.open } ) )",
28+
parserOptions: { ecmaVersion: 6 }
29+
},
30+
{
31+
code: "this.setState( ( state, props ) => ( { opened: ! props.open } ) )",
32+
parserOptions: { ecmaVersion: 6 }
33+
},
34+
{
35+
code: "var yolo = this.state.yolo; this.setState( ( state, props ) => ( { opened: ! props.open } ) )",
36+
parserOptions: { ecmaVersion: 6 }
37+
},
38+
],
39+
invalid: [
40+
{
41+
code: "this.setState( { opened: this.state.opened } )",
42+
errors: [{ message: "Do not access this.state or this.props in setState(), instead apply a function with signature ( state, [props] ) to setState and access state and props."}],
43+
parserOptions: { ecmaVersion: 6 }
44+
},
45+
{
46+
code: "this.setState( { opened: this.props.opened } )",
47+
errors: [{ message: "Do not access this.state or this.props in setState(), instead apply a function with signature ( state, [props] ) to setState and access state and props."}],
48+
parserOptions: { ecmaVersion: 6 }
49+
},
50+
{
51+
code: "this.setState( ( state ) => ( { opened: this.state.opened } ) )",
52+
errors: [{ message: "Do not access this.state or this.props in setState(), instead apply a function with signature ( state, [props] ) to setState and access state and props."}],
53+
parserOptions: { ecmaVersion: 6 }
54+
},
55+
{
56+
code: "this.setState( ( state ) => ( { opened: { inside: this.props.opened } } ) )",
57+
errors: [{ message: "Do not access this.state or this.props in setState(), instead apply a function with signature ( state, [props] ) to setState and access state and props."}],
58+
parserOptions: { ecmaVersion: 6 }
59+
},
60+
{
61+
code: "this.setState( ( state ) => { var taco = this.state.taco; return { opened: taco }; } )",
62+
errors: [{ message: "Do not access this.state or this.props in setState(), instead apply a function with signature ( state, [props] ) to setState and access state and props."}],
63+
parserOptions: { ecmaVersion: 6 }
64+
}
65+
]
66+
});

0 commit comments

Comments
 (0)