@@ -101,6 +101,48 @@ module.exports = {
101
101
return false ;
102
102
}
103
103
104
+ /**
105
+ * Check if the current node is in a setState updater method
106
+ * @return {boolean } true if we are in a setState updater, false if not
107
+ */
108
+ function inSetStateUpdater ( ) {
109
+ let scope = context . getScope ( ) ;
110
+ while ( scope ) {
111
+ if (
112
+ scope . block && scope . block . parent
113
+ && scope . block . parent . type === 'CallExpression'
114
+ && scope . block . parent . callee . property
115
+ && scope . block . parent . callee . property . name === 'setState'
116
+ // Make sure we are in the updater not the callback
117
+ && scope . block . parent . arguments [ 0 ] . start === scope . block . start
118
+ ) {
119
+ return true ;
120
+ }
121
+ scope = scope . upper ;
122
+ }
123
+ return false ;
124
+ }
125
+
126
+ function isPropArgumentInSetStateUpdater ( node ) {
127
+ let scope = context . getScope ( ) ;
128
+ while ( scope ) {
129
+ if (
130
+ scope . block && scope . block . parent
131
+ && scope . block . parent . type === 'CallExpression'
132
+ && scope . block . parent . callee . property
133
+ && scope . block . parent . callee . property . name === 'setState'
134
+ // Make sure we are in the updater not the callback
135
+ && scope . block . parent . arguments [ 0 ] . start === scope . block . start
136
+ && scope . block . parent . arguments [ 0 ] . params
137
+ && scope . block . parent . arguments [ 0 ] . params . length > 0
138
+ ) {
139
+ return scope . block . parent . arguments [ 0 ] . params [ 1 ] . name === node . object . name ;
140
+ }
141
+ scope = scope . upper ;
142
+ }
143
+ return false ;
144
+ }
145
+
104
146
/**
105
147
* Checks if we are using a prop
106
148
* @param {ASTNode } node The AST node being checked.
@@ -109,7 +151,8 @@ module.exports = {
109
151
function isPropTypesUsage ( node ) {
110
152
const isClassUsage = (
111
153
( utils . getParentES6Component ( ) || utils . getParentES5Component ( ) ) &&
112
- node . object . type === 'ThisExpression' && node . property . name === 'props'
154
+ ( ( node . object . type === 'ThisExpression' && node . property . name === 'props' )
155
+ || isPropArgumentInSetStateUpdater ( node ) )
113
156
) ;
114
157
const isStatelessFunctionUsage = node . object . name === 'props' ;
115
158
return isClassUsage || isStatelessFunctionUsage || inLifeCycleMethod ( ) ;
@@ -534,16 +577,20 @@ module.exports = {
534
577
const isDirectProp = DIRECT_PROPS_REGEX . test ( sourceCode . getText ( node ) ) ;
535
578
const isDirectNextProp = DIRECT_NEXT_PROPS_REGEX . test ( sourceCode . getText ( node ) ) ;
536
579
const isDirectPrevProp = DIRECT_PREV_PROPS_REGEX . test ( sourceCode . getText ( node ) ) ;
580
+ const isDirectSetStateProp = isPropArgumentInSetStateUpdater ( node ) ;
537
581
const isInClassComponent = utils . getParentES6Component ( ) || utils . getParentES5Component ( ) ;
538
582
const isNotInConstructor = ! inConstructor ( node ) ;
539
583
const isNotInLifeCycleMethod = ! inLifeCycleMethod ( ) ;
540
- if ( ( isDirectProp || isDirectNextProp || isDirectPrevProp )
584
+ const isNotInSetStateUpdater = ! inSetStateUpdater ( ) ;
585
+ if ( ( isDirectProp || isDirectNextProp || isDirectPrevProp || isDirectSetStateProp )
541
586
&& isInClassComponent
542
587
&& isNotInConstructor
543
- && isNotInLifeCycleMethod ) {
588
+ && isNotInLifeCycleMethod
589
+ && isNotInSetStateUpdater
590
+ ) {
544
591
return void 0 ;
545
592
}
546
- if ( ! isDirectProp && ! isDirectNextProp && ! isDirectPrevProp ) {
593
+ if ( ! isDirectProp && ! isDirectNextProp && ! isDirectPrevProp && ! isDirectSetStateProp ) {
547
594
node = node . parent ;
548
595
}
549
596
const property = node . property ;
@@ -607,6 +654,9 @@ module.exports = {
607
654
case 'FunctionExpression' :
608
655
type = 'destructuring' ;
609
656
properties = node . params [ 0 ] . properties ;
657
+ if ( inSetStateUpdater ( ) ) {
658
+ properties = node . params [ 1 ] . properties ;
659
+ }
610
660
break ;
611
661
case 'VariableDeclarator' :
612
662
for ( let i = 0 , j = node . id . properties . length ; i < j ; i ++ ) {
@@ -898,11 +948,20 @@ module.exports = {
898
948
markPropTypesAsDeclared ( node , resolveTypeAnnotation ( node . params [ 0 ] ) ) ;
899
949
}
900
950
951
+ function handleSetStateUpdater ( node ) {
952
+ if ( ! node . params || ! node . params . length || ! inSetStateUpdater ( ) ) {
953
+ return ;
954
+ }
955
+ markPropTypesAsUsed ( node ) ;
956
+ }
957
+
901
958
/**
959
+ * Handle both stateless functions and setState updater functions.
902
960
* @param {ASTNode } node We expect either an ArrowFunctionExpression,
903
961
* FunctionDeclaration, or FunctionExpression
904
962
*/
905
- function handleStatelessComponent ( node ) {
963
+ function handleFunctionLikeExpressions ( node ) {
964
+ handleSetStateUpdater ( node ) ;
906
965
markDestructuredFunctionArgumentsAsUsed ( node ) ;
907
966
markAnnotatedFunctionArgumentsAsDeclared ( node ) ;
908
967
}
@@ -942,11 +1001,11 @@ module.exports = {
942
1001
markPropTypesAsUsed ( node ) ;
943
1002
} ,
944
1003
945
- FunctionDeclaration : handleStatelessComponent ,
1004
+ FunctionDeclaration : handleFunctionLikeExpressions ,
946
1005
947
- ArrowFunctionExpression : handleStatelessComponent ,
1006
+ ArrowFunctionExpression : handleFunctionLikeExpressions ,
948
1007
949
- FunctionExpression : handleStatelessComponent ,
1008
+ FunctionExpression : handleFunctionLikeExpressions ,
950
1009
951
1010
MemberExpression : function ( node ) {
952
1011
let type ;
0 commit comments