From bc484bd582016bcf8a1e7960df70d64d09d76192 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Mon, 3 Jul 2023 12:59:40 -0400 Subject: [PATCH] [ESLint] Disallow hooks in async functions Hooks cannot be called in async functions, on either the client or the server. This mistake sometimes happens when using Server Components, especially when refactoring a Server Component to a Client Component. React logs a warning at runtime, but it's even better to catch this with a lint rule since it will show immediate inline feedback in the editor. I added this to the existing "Rules of Hooks" ESLint rule. --- .../__tests__/ESLintRulesOfHooks-test.js | 30 +++++++++++++++++++ .../src/RulesOfHooks.js | 11 +++++++ 2 files changed, 41 insertions(+) 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. //