Skip to content

Commit 79b56b4

Browse files
Evgueni Navernioukyannickcr
authored andcommitted
Fix false positive in no-unused-prop-types (fixes #801)
1 parent bb5b1df commit 79b56b4

File tree

2 files changed

+429
-3
lines changed

2 files changed

+429
-3
lines changed

lib/rules/no-unused-prop-types.js

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,53 @@ module.exports = {
174174
);
175175
}
176176

177+
/**
178+
* Returns true if the given node is a React Component lifecycle method
179+
* @param {ASTNode} node The AST node being checked.
180+
* @return {Boolean} True if the node is a lifecycle method
181+
*/
182+
function isNodeALifeCycleMethod(node) {
183+
var nodeKeyName = (node.key || {}).name;
184+
return (
185+
node.kind === 'constructor' ||
186+
nodeKeyName === 'componentWillReceiveProps' ||
187+
nodeKeyName === 'shouldComponentUpdate' ||
188+
nodeKeyName === 'componentWillUpdate' ||
189+
nodeKeyName === 'componentDidUpdate'
190+
);
191+
}
192+
193+
/**
194+
* Returns true if the given node is inside a React Component lifecycle
195+
* method.
196+
* @param {ASTNode} node The AST node being checked.
197+
* @return {Boolean} True if the node is inside a lifecycle method
198+
*/
199+
function isInLifeCycleMethod(node) {
200+
if (node.type === 'MethodDefinition' && isNodeALifeCycleMethod(node)) {
201+
return true;
202+
}
203+
204+
if (node.parent) {
205+
return isInLifeCycleMethod(node.parent);
206+
}
207+
208+
return false;
209+
}
210+
211+
/**
212+
* Checks if a prop init name matches common naming patterns
213+
* @param {ASTNode} node The AST node being checked.
214+
* @returns {Boolean} True if the prop name matches
215+
*/
216+
function isPropAttributeName (node) {
217+
return (
218+
node.init.name === 'props' ||
219+
node.init.name === 'nextProps' ||
220+
node.init.name === 'prevProps'
221+
);
222+
}
223+
177224
/**
178225
* Checks if a prop is used
179226
* @param {ASTNode} node The AST node being checked.
@@ -536,11 +583,14 @@ module.exports = {
536583
node.id.properties[i].value.type === 'ObjectPattern'
537584
);
538585
// let {firstname} = props
539-
var statelessDestructuring = node.init.name === 'props' && utils.getParentStatelessComponent();
586+
var genericDestructuring = isPropAttributeName(node) && (
587+
utils.getParentStatelessComponent() ||
588+
isInLifeCycleMethod(node)
589+
);
540590

541591
if (thisDestructuring) {
542592
properties = node.id.properties[i].value.properties;
543-
} else if (statelessDestructuring) {
593+
} else if (genericDestructuring) {
544594
properties = node.id.properties;
545595
} else {
546596
continue;
@@ -776,7 +826,10 @@ module.exports = {
776826
// let {props: {firstname}} = this
777827
var thisDestructuring = destructuring && node.init.type === 'ThisExpression';
778828
// let {firstname} = props
779-
var statelessDestructuring = destructuring && node.init.name === 'props' && utils.getParentStatelessComponent();
829+
var statelessDestructuring = destructuring && isPropAttributeName(node) && (
830+
utils.getParentStatelessComponent() ||
831+
isInLifeCycleMethod(node)
832+
);
780833

781834
if (!thisDestructuring && !statelessDestructuring) {
782835
return;
@@ -831,6 +884,18 @@ module.exports = {
831884
}
832885
},
833886

887+
ObjectPattern: function(node) {
888+
// If the object pattern is a destructured props object in a lifecycle
889+
// method -- mark it for used props.
890+
if (isNodeALifeCycleMethod(node.parent.parent)) {
891+
node.properties.forEach(function(property, i) {
892+
if (i === 0) {
893+
markPropTypesAsUsed(node.parent);
894+
}
895+
});
896+
}
897+
},
898+
834899
ObjectExpression: function(node) {
835900
// Search for the proptypes declaration
836901
node.properties.forEach(function(property) {

0 commit comments

Comments
 (0)