@@ -28,6 +28,9 @@ export default {
28
28
additionalHooks : {
29
29
type : 'string' ,
30
30
} ,
31
+ avoidObjects : {
32
+ type : 'boolean' ,
33
+ } ,
31
34
} ,
32
35
} ,
33
36
] ,
@@ -41,8 +44,16 @@ export default {
41
44
? new RegExp ( context . options [ 0 ] . additionalHooks )
42
45
: undefined ;
43
46
47
+ const avoidObjects =
48
+ context . options &&
49
+ context . options [ 0 ] &&
50
+ context . options [ 0 ] . avoidObjects
51
+ ? true
52
+ : false ;
53
+
44
54
const options = {
45
55
additionalHooks,
56
+ avoidObjects,
46
57
} ;
47
58
48
59
function reportProblem ( problem ) {
@@ -68,7 +79,7 @@ export default {
68
79
reportProblem ( {
69
80
node : declaredDependenciesNode ,
70
81
message :
71
- `React Hook ${ context . getSource ( reactiveHook ) } was passed a ` +
82
+ `React Hook ${ reactiveHookName } was passed a ` +
72
83
'dependency list that is not an array literal. This means we ' +
73
84
"can't statically verify whether you've passed the correct " +
74
85
'dependencies.' ,
@@ -79,9 +90,25 @@ export default {
79
90
if ( declaredDependencyNode === null ) {
80
91
return ;
81
92
}
93
+ if ( declaredDependencyNode . type === 'Identifier' && options && options . avoidObjects ) {
94
+ // If we see an object then add a special warning if avoidObjects option is true.
82
95
reportProblem ( {
83
96
node : declaredDependencyNode ,
84
97
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 !!
85
112
} ) ;
86
113
return ;
87
114
}
@@ -105,9 +132,8 @@ export default {
105
132
const isEffect = / E f f e c t ( $ | [ ^ a - z ] ) / g. test ( reactiveHookName ) ;
106
133
107
134
// 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 ;
111
137
112
138
switch ( callback . type ) {
113
139
case 'FunctionExpression' :
0 commit comments