1
1
/**
2
2
* @fileoverview Prevent usage of this.state within setState
3
- * @author Rolf Erik Lekang
3
+ * @author Rolf Erik Lekang, Jørgen Aaberg
4
4
*/
5
5
6
6
'use strict' ;
@@ -21,23 +21,117 @@ module.exports = {
21
21
create : function ( context ) {
22
22
function isSetStateCall ( node ) {
23
23
return node . type === 'CallExpression' &&
24
- node . callee &&
25
24
node . callee . property &&
26
- node . callee . property . name === 'setState' ;
25
+ node . callee . property . name === 'setState' &&
26
+ node . callee . object . type === 'ThisExpression' ;
27
27
}
28
28
29
+ // The methods array contains all methods or functions that are using this.state
30
+ // or that are calling another method or function using this.state
31
+ const methods = [ ] ;
32
+ // The vars array contains all variables that contains this.state
33
+ const vars = [ ] ;
29
34
return {
30
- ThisExpression : function ( node ) {
31
- var memberExpression = node . parent ;
32
- if ( memberExpression . property . name === 'state' ) {
33
- var current = memberExpression ;
35
+ CallExpression ( node ) {
36
+ // Appends all the methods that are calling another
37
+ // method containg this.state to the methods array
38
+ methods . map ( method => {
39
+ if ( node . callee . name === method . methodName ) {
40
+ let current = node . parent ;
41
+ while ( current . type !== 'Program' ) {
42
+ if ( current . type === 'MethodDefinition' ) {
43
+ methods . push ( {
44
+ methodName : current . key . name ,
45
+ node : method . node
46
+ } ) ;
47
+ break ;
48
+ }
49
+ current = current . parent ;
50
+ }
51
+ }
52
+ } ) ;
53
+
54
+ // Finding all CallExpressions that is inside a setState
55
+ // to further check if they contains this.state
56
+ let current = node . parent ;
57
+ while ( current . type !== 'Program' ) {
58
+ if ( isSetStateCall ( current ) ) {
59
+ const methodName = node . callee . name ;
60
+ methods . map ( method => {
61
+ if ( method . methodName === methodName ) {
62
+ context . report (
63
+ method . node ,
64
+ 'Use callback in setState when referencing the previous state.'
65
+ ) ;
66
+ }
67
+ } ) ;
68
+
69
+ break ;
70
+ }
71
+ current = current . parent ;
72
+ }
73
+ } ,
74
+
75
+ MemberExpression ( node ) {
76
+ if (
77
+ node . property . name === 'state' &&
78
+ node . object . type === 'ThisExpression'
79
+ ) {
80
+ let current = node ;
34
81
while ( current . type !== 'Program' ) {
82
+ // Reporting if this.state is directly within this.setState
35
83
if ( isSetStateCall ( current ) ) {
36
- context . report ( {
37
- node : memberExpression ,
38
- message : 'Use callback in setState when referencing the previous state.'
84
+ context . report (
85
+ node ,
86
+ 'Use callback in setState when referencing the previous state.'
87
+ ) ;
88
+ break ;
89
+ }
90
+
91
+ // Storing all functions and methods that contains this.state
92
+ if ( current . type === 'MethodDefinition' ) {
93
+ methods . push ( {
94
+ methodName : current . key . name ,
95
+ node : node
39
96
} ) ;
40
97
break ;
98
+ } else if ( current . type === 'FunctionExpression' ) {
99
+ methods . push ( {
100
+ methodName : current . parent . key . name ,
101
+ node : node
102
+ } ) ;
103
+ break ;
104
+ }
105
+
106
+ // Storing all variables containg this.state
107
+ if ( current . type === 'VariableDeclarator' ) {
108
+ vars . push ( {
109
+ node : node ,
110
+ scope : context . getScope ( )
111
+ } ) ;
112
+ break ;
113
+ }
114
+
115
+ current = current . parent ;
116
+ }
117
+ }
118
+ } ,
119
+
120
+ Identifier ( node ) {
121
+ // Checks if the identifier is a variable within an object
122
+ let current = node ;
123
+ while ( current . parent . type === 'BinaryExpression' ) {
124
+ current = current . parent ;
125
+ }
126
+ if ( current . parent . value === current ) {
127
+ while ( current . type !== 'Program' ) {
128
+ if ( isSetStateCall ( current ) ) {
129
+ vars
130
+ . filter ( v => v . scope === context . getScope ( ) )
131
+ . map ( v => context . report (
132
+ v . node ,
133
+ 'Use callback in setState when referencing the previous state.'
134
+ ) ) ;
41
135
}
42
136
current = current . parent ;
43
137
}
0 commit comments