React Hooks Explained
What Hooks are, why they were introduced, and the two rules that govern how you're allowed to use them.
2 min read
Hooks are functions, all prefixed with use, that let function components tap into React features — state, side effects, refs, context — that used to require writing a class component. useState and useEffect are the two you'll use constantly; useRef, useContext, and others cover more specific needs.
Why Hooks exist
Before Hooks (introduced in React 16.8, in 2019), state and lifecycle methods (componentDidMount, componentDidUpdate) only existed on class components. This pushed people toward classes for anything stateful, and made it hard to reuse stateful logic between components — you couldn't extract "fetch data and track loading state" into a reusable class the way you can with a function. Hooks let plain function components hold state and run side effects, and let you extract and reuse that logic as a plain function (a custom hook, covered later in this course).
// Before Hooks — class component
class Counter extends React.Component {
state = { count: 0 };
render() {
return (
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
{this.state.count}
</button>
);
}
}
// With Hooks — function component
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}Both work today — React still supports class components — but virtually all new code is written with Hooks, and most libraries and docs assume them.
Rule 1: only call Hooks at the top level
Never call a Hook inside a condition, loop, or nested function:
// Wrong
function Profile({ userId }) {
if (userId) {
const [name, setName] = useState(""); // breaks React's tracking
}
}React matches up Hook calls between renders by the order they're called in, not by name — internally it's just an array, walked in sequence on every render. Call a Hook conditionally, and that order can shift between renders, silently attaching the wrong piece of state to the wrong useState call. Always call every Hook unconditionally, every render, in the same order.
Rule 2: only call Hooks from React functions
Call Hooks from function components or from custom hooks — never from a regular JavaScript function, a class component, or an event handler:
function handleClick() {
const [x, setX] = useState(0); // Wrong — not a component or a hook
}This keeps all stateful logic traceable to a component, which is what lets React associate the right state with the right component instance across re-renders.
Both rules are enforced in practice by the eslint-plugin-react-hooks linter, which ships by default in Vite's React template and will flag violations before they become bugs.
The next two lessons cover the two Hooks you'll reach for constantly: useState for state, and useEffect for side effects.