@samitouri / QOS-React / commits / 99efc627a5

[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