[eslint] Add an option to require dependencies on effect hooks (#33344)
Summary: To prepare for automatic effect dependencies, some codebases may want to codemod existing useEffect calls with no deps to include an explicit undefined second argument in order to preserve the "run on every render" behavior. In sufficiently large codebases, this may require a temporary enforcement period where all effects provide an explicit dependencies argument. Outside of migration, relying on a component to render can lead to real bugs, especially when working with memoization.
Jordan Brown committed
May 23, 2025 at 10:09 UTC
99efc627a5a8cb56f50cfffee544c86c49572b6f
2 files changed
+32
packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js
+17
@@ -8344,6 +8344,23 @@ const testsTypescript = {
8344
},
8345
],
8346
},
8347
+ {
8348
+ code: normalizeIndent`
8349
+ function MyComponent(props) {
8350
+ useEffect(() => {
8351
+ console.log(props.foo);
8352
+ });
8353
+ }
8354
+ `,
8355
+ options: [{requireExplicitEffectDeps: true}],
8356
+ errors: [
8357
+ {
8358
+ message:
8359
+ 'React Hook useEffect always requires dependencies. Please add a dependency array or an explicit `undefined`',
8360
+ suggestions: undefined,
8361
+ },
8362
+ ],
8363
+ },
8364
],
8365
};
8366
packages/eslint-plugin-react-hooks/src/rules/ExhaustiveDeps.ts
+15
@@ -67,6 +67,9 @@ const rule = {
67
type: 'string',
68
},
69
},
70
+ requireExplicitEffectDeps: {
71
+ type: 'boolean',
72
+ }
73
},
74
},
75
],
@@ -90,10 +93,13 @@ const rule = {
93
? rawOptions.experimental_autoDependenciesHooks
94
: [];
95
96
+ const requireExplicitEffectDeps: boolean = rawOptions && rawOptions.requireExplicitEffectDeps || false;
97
+
98
const options = {
99
additionalHooks,
100
experimental_autoDependenciesHooks,
101
enableDangerousAutofixThisMayCauseInfiniteLoops,
102
+ requireExplicitEffectDeps,
103
};
104
105
function reportProblem(problem: Rule.ReportDescriptor) {
@@ -1340,6 +1346,15 @@ const rule = {
1346
return;
1347
}
1348
1349
+ if (!maybeNode && isEffect && options.requireExplicitEffectDeps) {
1350
+ reportProblem({
1351
+ node: reactiveHook,
1352
+ message:
1353
+ `React Hook ${reactiveHookName} always requires dependencies. ` +
1354
+ `Please add a dependency array or an explicit \`undefined\``
1355
+ });
1356
+ }
1357
+
1358
const isAutoDepsHook =
1359
options.experimental_autoDependenciesHooks.includes(reactiveHookName);
1360