@@ -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