@@ -8,6 +8,7 @@ const Components = require('../util/Components');
88const docsUrl = require ( '../util/docsUrl' ) ;
99const isAssignmentLHS = require ( '../util/ast' ) . isAssignmentLHS ;
1010const report = require ( '../util/report' ) ;
11+ const testReactVersion = require ( '../util/version' ) . testReactVersion ;
1112
1213const DEFAULT_OPTION = 'always' ;
1314
@@ -94,6 +95,8 @@ module.exports = {
9495 const destructureInSignature = ( context . options [ 1 ] && context . options [ 1 ] . destructureInSignature ) || 'ignore' ;
9596 const sfcParams = createSFCParams ( ) ;
9697
98+ // set to save renamed var of useContext
99+ const contextSet = new Set ( ) ;
97100 /**
98101 * @param {ASTNode } node We expect either an ArrowFunctionExpression,
99102 * FunctionDeclaration, or FunctionExpression
@@ -128,7 +131,7 @@ module.exports = {
128131 function handleSFCUsage ( node ) {
129132 const propsName = sfcParams . propsName ( ) ;
130133 const contextName = sfcParams . contextName ( ) ;
131- // props.aProp || context.aProp
134+ // props.aProp
132135 const isPropUsed = (
133136 ( propsName && node . object . name === propsName )
134137 || ( contextName && node . object . name === contextName )
@@ -142,6 +145,16 @@ module.exports = {
142145 } ,
143146 } ) ;
144147 }
148+
149+ // const foo = useContext(aContext);
150+ // foo.aProp
151+ const isContextUsed = contextSet . has ( node . object . name ) && ! isAssignmentLHS ( node ) ;
152+ if ( isContextUsed && configuration === 'always' ) {
153+ context . report ( {
154+ node,
155+ message : `Must use destructuring ${ node . object . name } assignment` ,
156+ } ) ;
157+ }
145158 }
146159
147160 function isInClassProperty ( node ) {
@@ -176,8 +189,9 @@ module.exports = {
176189 }
177190 }
178191
179- return {
192+ const hasHooks = testReactVersion ( context , '>= 16.9' ) ;
180193
194+ return {
181195 FunctionDeclaration : handleStatelessComponent ,
182196
183197 ArrowFunctionExpression : handleStatelessComponent ,
@@ -212,13 +226,29 @@ module.exports = {
212226 const SFCComponent = components . get ( context . getScope ( node ) . block ) ;
213227
214228 const destructuring = ( node . init && node . id && node . id . type === 'ObjectPattern' ) ;
229+ const identifier = ( node . init && node . id && node . id . type === 'Identifier' ) ;
215230 // let {foo} = props;
216- const destructuringSFC = destructuring && ( node . init . name === 'props' || node . init . name === 'context' ) ;
231+ const destructuringSFC = destructuring && node . init . name === 'props' ;
232+ // let {foo} = useContext(aContext);
233+ const destructuringUseContext = hasHooks && destructuring && node . init . callee && node . init . callee . name === 'useContext' ;
234+ // let foo = useContext(aContext);
235+ const assignUseContext = hasHooks && identifier && node . init . callee && node . init . callee . name === 'useContext' ;
217236 // let {foo} = this.props;
218237 const destructuringClass = destructuring && node . init . object && node . init . object . type === 'ThisExpression' && (
219238 node . init . property . name === 'props' || node . init . property . name === 'context' || node . init . property . name === 'state'
220239 ) ;
221240
241+ if ( SFCComponent && assignUseContext ) {
242+ contextSet . add ( node . id . name ) ;
243+ }
244+
245+ if ( SFCComponent && destructuringUseContext && configuration === 'never' ) {
246+ context . report ( {
247+ node,
248+ message : `Must never use destructuring ${ node . init . callee . name } assignment` ,
249+ } ) ;
250+ }
251+
222252 if ( SFCComponent && destructuringSFC && configuration === 'never' ) {
223253 report ( context , messages . noDestructAssignment , 'noDestructAssignment' , {
224254 node,
0 commit comments