[ESLint] Disallow hooks in async functions (#27045)

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.
This commit is contained in:
Andrew Clark
2023-07-05 11:44:28 -04:00
committed by GitHub
parent 6b478c1b2e
commit aa57ded93d
2 changed files with 41 additions and 0 deletions
@@ -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 = [];
@@ -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.
//