diff --git a/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js b/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js index 7b90afb75a742..fd0ad9b046c20 100644 --- a/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js +++ b/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js @@ -1042,6 +1042,22 @@ const tests = { `, errors: [classError('useState')], }, + { + code: normalizeIndent` + async function AsyncComponent() { + useState(); + } + `, + errors: [asyncComponentHookError('useState')], + }, + { + code: normalizeIndent` + async function useAsyncHook() { + useState(); + } + `, + errors: [asyncComponentHookError('useState')], + }, ], }; @@ -1300,6 +1316,14 @@ if (__EXPERIMENTAL__) { `, errors: [classError('use')], }, + { + code: normalizeIndent` + async function AsyncComponent() { + use(); + } + `, + errors: [asyncComponentHookError('use')], + }, ]; } @@ -1368,6 +1392,12 @@ function useEffectEventError(fn) { }; } +function asyncComponentHookError(fn) { + return { + message: `React Hook "${fn}" cannot be called in an async function.`, + }; +} + // For easier local testing if (!process.env.CI) { let only = []; diff --git a/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js b/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js index e6a71cec116b0..9818d18938310 100644 --- a/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js +++ b/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js @@ -485,6 +485,17 @@ export default { // Pick a special message depending on the scope this hook was // called in. if (isDirectlyInsideComponentOrHook) { + // Report an error if the hook is called inside an async function. + const isAsyncFunction = codePathNode.async; + if (isAsyncFunction) { + context.report({ + node: hook, + message: + `React Hook "${context.getSource(hook)}" cannot be ` + + 'called in an async function.', + }); + } + // Report an error if a hook does not reach all finalizing code // path segments. //