react-hooks/rules-of-hooks
Require hooks to be called at the top level of a component or a custom hook
Also called react_hooks/rules-of-hooks, react/rules-of-hooks
React tells one useState from the next by the order you call them in. If a hook runs on one render but not the next, every hook after it gets another hook's state, and React throws "Rendered fewer hooks than expected".
So you must call every hook on every render, in the same order. You can't call one in a condition, a loop, a nested function, or after an early return. You also can't call one in a class, an async function, at the top level of a file, or in a function that's neither a component nor a hook. A name counts as a hook if it's use or starts with use and a capital letter. A component's name starts with a capital letter.
This rule is on by default if your package.json depends on react.
Configure#
On by default as an error. To turn it off:
[lint.rules]
"react-hooks/rules-of-hooks" = "off" # or "warn"Every option, set to its default. The ones that are commented out are unset by default.
[lint.rules]
"react-hooks/rules-of-hooks" = ["error", {
# additionalHooks = <string>,
}]Examples#
function Profile({ user }) {
if (user) {
const [editing, setEditing] = useStateReact Hook useState is called conditionally. React Hooks must be called in the exact same order in every component render.(false);
}
return <p>{user?.name}</p>;
}function Profile({ user }) {
if (!user) {
return null;
}
const [editing, setEditing] = useStateReact Hook useState is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?(false);
return <p onClick={() => setEditing(!editing)}>{user.name}</p>;
}function List({ items }) {
return items.map(item => {
const id = useIdReact Hook useId cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.();
return <li key={id}>{item.name}</li>;
});
}function loadTheme() {
return useContextReact Hook useContext is called in function loadTheme that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word use.(ThemeContext);
}function Profile({ user }) {
const [editing, setEditing] = useState(false);
if (!user) {
return null;
}
return <p onClick={() => setEditing(!editing)}>{user.name}</p>;
}
function useTheme() {
return useContext(ThemeContext);
}use is the exception. You can call it in a condition or a loop, but not in a try block:
function Note({ notePromise }) {
if (!notePromise) {
return null;
}
const note = use(notePromise);
return <p>{note.text}</p>;
}A use that the file declares itself isn't React's, unless it's imported from "react". Playwright passes a function with that name to every fixture:
import { test as base } from "@playwright/test";
export const test = base.extend({
user: async ({ page }, use) => {
await page.goto("/login");
await use("alice");
},
});In a *.stories.* file, a function named render counts as a component, because Storybook renders it as one:
export const Controlled = {
render: args => {
const [on, setOn] = useState(false);
return <Button {...args} active={on} onClick={() => setOn(!on)} />;
},
};With an ESLint config file both are reported, as eslint-plugin-react-hooks does.
You can only call a function made with useEffectEvent from an effect or another effect event in the same component:
function Chat({ room }) {
const onConnected = useEffectEvent(() => {
notify(room);
});
return <button onClick={onConnectedonConnected is a function created with React Hook useEffectEvent, and can only be called from Effects and Effect Events in the same component. It cannot be assigned to a variable or passed down.}>Connect</button>;
}Your own effect hooks count as effects if the additionalEffectHooks setting is a regular expression that matches their names. It goes under [lint.settings.react-hooks] in bunfig.toml, or under settings["react-hooks"] in an ESLint config.
Options#
| Option | What it does |
|---|---|
additionalHooks | Accepted, but this rule doesn't use it. |