react-hooks/hooks
Require hooks to be called at the top level of a component or hook, never conditionally
React tells one useState from the next by the order the hooks are called in. That order must be the same on every render. A hook inside an if, a loop, or after an early return runs on some renders and not on others. Then state ends up with the wrong hook, or React throws.
The rule also reports a hook that's called inside a nested function, and a hook that's passed around as a value instead of being called.
Configure#
Off by default. To turn it on:
bunfig.toml
[lint.rules]
"react-hooks/hooks" = "error" # or "warn"Examples#
Reported:
import { useState } from "react";
function Profile({ user }) {
if (!user) {
return null;
}
const [name, setName] = useState(user.name);
return <input value={name} onChange={event => setName(event.target.value)} />;
}import { useState } from "react";
function UserList({ users }) {
const rows = users.map(user => {
const [open] = useState(false);
return <li key={user.id}>{open ? user.bio : user.name}</li>;
});
return <ul>{rows}</ul>;
}import { useState } from "react";
import { useStoredState } from "./storage";
function Field({ persist }) {
const useValue = persist ? useStoredState : useState;
const [value] = useValue("");
return <input value={value} readOnly />;
}Not reported:
import { useState } from "react";
function Profile({ user }) {
const [name, setName] = useState(user ? user.name : "");
if (!user) {
return null;
}
return <input value={name} onChange={event => setName(event.target.value)} />;
}When not to use it#
react-hooks/rules-of-hooks finds most of the same mistakes and is on by default in React projects. You rarely need both.