Rules
rules-of-hooks
Full Name in @eslint-react/eslint-plugin@beta
@eslint-react/rules-of-hooksFull Name in eslint-plugin-react-x@beta
react-x/rules-of-hooksPresets
x
recommended
recommended-typescript
recommended-type-checked
strict
strict-typescript
strict-type-checked
Description
Enforces the Rules of Hooks.
React Hooks must be called at the top level of a React function component or a custom Hook. They must not be called inside conditions, loops, nested functions, after early returns, in async functions, in class components, or at module level.
The special use() hook is an exception — it is allowed inside conditionals and loops, but not inside try/catch blocks.
Examples
Passing
import { useState, useEffect } from "react";
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(count);
}, [count]);
return <div>{count}</div>;
}Passing
import { useState } from "react";
function useCustomHook() {
const [value, setValue] = useState("");
return [value, setValue] as const;
}Passing
import { use } from "react";
function MyComponent({ shouldFetch, promise }) {
if (shouldFetch) {
const data = use(promise);
return <div>{data}</div>;
}
return <div>No data</div>;
}Failing
import { useState } from "react";
function MyComponent({ condition }) {
// 🔴 React Hook 'useState' is called conditionally.
if (condition) {
const [value, setValue] = useState("");
}
return null;
}Failing
import { useEffect } from "react";
function MyComponent({ items }) {
// 🔴 React Hook 'useEffect' may be executed more than once.
for (const item of items) {
useEffect(() => {
console.log(item);
}, [item]);
}
return null;
}Failing
import { useState, useEffect } from "react";
function MyComponent({ condition }) {
if (condition) {
return null;
}
// 🔴 React Hook 'useState' is called after an early return.
const [value, setValue] = useState("");
return <div>{value}</div>;
}Failing
import { useState } from "react";
function MyComponent() {
const handleClick = () => {
// 🔴 React Hook 'useState' is called in a nested function.
const [value, setValue] = useState("");
};
return <button onClick={handleClick}>Click</button>;
}Failing
import { useState } from "react";
// 🔴 React Hook 'useState' is called in an async function.
async function useAsyncHook() {
const [value, setValue] = useState("");
return value;
}Failing
import { useState } from "react";
class MyComponent {
render() {
// 🔴 React Hook 'useState' cannot be called in a class component.
const [value, setValue] = useState("");
return null;
}
}Failing
import { useState } from "react";
// 🔴 React Hook 'useState' cannot be called at the top level.
const [value, setValue] = useState("");Failing
import { use } from "react";
function MyComponent({ promise }) {
try {
// 🔴 React Hook 'use' cannot be called inside a try/catch block.
const data = use(promise);
return <div>{data}</div>;
} catch (e) {
return <div>Error</div>;
}
}Implementation
Further Reading
See Also
exhaustive-deps
Enforces that React hook dependency arrays contain all reactive values used in the callback.