diff --git a/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js b/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js index 8c5eeb0045..555c54148e 100644 --- a/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js +++ b/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js @@ -8344,6 +8344,23 @@ const testsTypescript = { }, ], }, + { + code: normalizeIndent` + function MyComponent(props) { + useEffect(() => { + console.log(props.foo); + }); + } + `, + options: [{requireExplicitEffectDeps: true}], + errors: [ + { + message: + 'React Hook useEffect always requires dependencies. Please add a dependency array or an explicit `undefined`', + suggestions: undefined, + }, + ], + }, ], }; diff --git a/packages/eslint-plugin-react-hooks/src/rules/ExhaustiveDeps.ts b/packages/eslint-plugin-react-hooks/src/rules/ExhaustiveDeps.ts index 624d28e3b3..d59a1ff792 100644 --- a/packages/eslint-plugin-react-hooks/src/rules/ExhaustiveDeps.ts +++ b/packages/eslint-plugin-react-hooks/src/rules/ExhaustiveDeps.ts @@ -67,6 +67,9 @@ const rule = { type: 'string', }, }, + requireExplicitEffectDeps: { + type: 'boolean', + } }, }, ], @@ -90,10 +93,13 @@ const rule = { ? rawOptions.experimental_autoDependenciesHooks : []; + const requireExplicitEffectDeps: boolean = rawOptions && rawOptions.requireExplicitEffectDeps || false; + const options = { additionalHooks, experimental_autoDependenciesHooks, enableDangerousAutofixThisMayCauseInfiniteLoops, + requireExplicitEffectDeps, }; function reportProblem(problem: Rule.ReportDescriptor) { @@ -1340,6 +1346,15 @@ const rule = { return; } + if (!maybeNode && isEffect && options.requireExplicitEffectDeps) { + reportProblem({ + node: reactiveHook, + message: + `React Hook ${reactiveHookName} always requires dependencies. ` + + `Please add a dependency array or an explicit \`undefined\`` + }); + } + const isAutoDepsHook = options.experimental_autoDependenciesHooks.includes(reactiveHookName); diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index ecf22aaad0..fd74620975 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -908,7 +908,7 @@ export function scheduleUpdateOnFiber( markRootUpdated(root, lane); if ( - (executionContext & RenderContext) !== NoLanes && + (executionContext & RenderContext) !== NoContext && root === workInProgressRoot ) { // This update was dispatched during the render phase. This is a mistake