diff --git a/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js b/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js index 3d60a36824d2..f25f10707a40 100644 --- a/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js +++ b/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js @@ -949,6 +949,23 @@ const allTests = { `, errors: [conditionalError('useConditionalHook')], }, + { + code: normalizeIndent` + function Component() { + const items = [1, 2]; + items.map(useCustomHook); + } + `, + errors: [hookArgumentError('useCustomHook')], + }, + { + code: normalizeIndent` + function Component() { + doSomething(React.useState); + } + `, + errors: [hookArgumentError('React.useState')], + }, { code: normalizeIndent` Hook.useState(); @@ -2062,6 +2079,14 @@ function genericError(hook) { }; } +function hookArgumentError(hook) { + return { + message: + `React Hook "${hook}" cannot be passed as an argument. React Hooks ` + + 'must be called in a React function component or a custom React Hook function.', + }; +} + function topLevelError(hook) { return { message: diff --git a/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts b/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts index ca82c99e2f55..e46c3aac774f 100644 --- a/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts +++ b/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts @@ -767,6 +767,25 @@ const rule = { reactHooks.push(node.callee); } + if (isInsideComponentOrHook(node)) { + for (const argument of node.arguments) { + if ( + argument && + argument.type !== 'SpreadElement' && + isHook(argument) + ) { + context.report({ + node: argument, + message: + `React Hook "${getSourceCode().getText( + argument, + )}" cannot be passed as an argument. React Hooks ` + + 'must be called in a React function component or a custom React Hook function.', + }); + } + } + } + // useEffectEvent: useEffectEvent functions can be passed by reference within useEffect as well as in // another useEffectEvent // Check all `useEffect` and `React.useEffect`, `useEffectEvent`, and `React.useEffectEvent`