react-hooks/hooks

Require hooks to be called at the top level of a component or hook, never conditionally

Also called react_hooks/hooks, react/hooks

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#

example.jsx1 problem
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)} />;
}
example.jsx1 problem
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>;
}
example.jsx4 problems
import { useState } from "react";
import { useStoredState } from "./storage";

function Field({ persist }) {
  const useValue = persist ? useStoredState : useState;
  const [value] = useValue("");
  return <input value={value} readOnly />;
}
example.jsxNo problems
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.