@@ -28,6 +28,9 @@ export default {
2828 additionalHooks : {
2929 type : 'string' ,
3030 } ,
31+ avoidObjects : {
32+ type : 'boolean' ,
33+ } ,
3134 } ,
3235 } ,
3336 ] ,
@@ -41,8 +44,16 @@ export default {
4144 ? new RegExp ( context . options [ 0 ] . additionalHooks )
4245 : undefined ;
4346
47+ const avoidObjects =
48+ context . options &&
49+ context . options [ 0 ] &&
50+ context . options [ 0 ] . avoidObjects
51+ ? true
52+ : false ;
53+
4454 const options = {
4555 additionalHooks,
56+ avoidObjects,
4657 } ;
4758
4859 function reportProblem ( problem ) {
@@ -68,7 +79,7 @@ export default {
6879 reportProblem ( {
6980 node : declaredDependenciesNode ,
7081 message :
71- `React Hook ${ context . getSource ( reactiveHook ) } was passed a ` +
82+ `React Hook ${ reactiveHookName } was passed a ` +
7283 'dependency list that is not an array literal. This means we ' +
7384 "can't statically verify whether you've passed the correct " +
7485 'dependencies.' ,
@@ -79,9 +90,25 @@ export default {
7990 if ( declaredDependencyNode === null ) {
8091 return ;
8192 }
93+ if ( declaredDependencyNode . type === 'Identifier' && options && options . avoidObjects ) {
94+ // If we see an object then add a special warning if avoidObjects option is true.
8295 reportProblem ( {
8396 node : declaredDependencyNode ,
8497 message :
98+ `React Hook ${ reactiveHookName } has an object in its ` +
99+ `dependency array: '${ declaredDependencyNode . name } '. ` +
100+ // `Non-primitive dependencies can result in triggering the ${reactiveHookName} unnecessarily ` +
101+ "Non-primitive dependencies can result in triggering " +
102+ "the callback unnecessarily due to referential equality " + // via Object.is()
103+ "comparison. Consider destructuring the object outside " +
104+ `the ${ reactiveHookName } call or using property accessors ` +
105+ "to refer to primitive values within the dependency array." ,
106+ // `destructure the object outside of the ${reactiveHookName} call and ` +
107+ // "refer to those specific values inside ${ context.getSource(reactiveHook) }.`;
108+ //
109+ // "equality. Either use a property accessor to extract the primitive value " +
110+ // `or convert the ${context.getSource(reactiveHook)} to a ${context.getSource(reactiveHook).replace('use', 'useDeepCompare')}.`,
111+ // ^!! Don't recommend useDeepCompare per Dan Abramov: https://twitter.com/dan_abramov/status/1104414469629898754 !!
85112 } ) ;
86113 return ;
87114 }
@@ -105,9 +132,8 @@ export default {
105132 const isEffect = / E f f e c t ( $ | [ ^ a - z ] ) / g. test ( reactiveHookName ) ;
106133
107134 // Check the declared dependencies for this reactive hook. If there is no
108- // second argument then the reactive callback will re-run on every render.
109- // So no need to check for dependency inclusion.
110- if ( ! declaredDependenciesNode && ! isEffect ) {
135+ // second argument then there are no declared dependencies.
136+ if ( ! declaredDependenciesNode ) return ;
111137
112138 switch ( callback . type ) {
113139 case 'FunctionExpression' :
0 commit comments