Rules of Hooks
React Hooks, introduced in React 16.8, revolutionized how we manage state and side effects in functional components. To ensure they work correctly and predictably, React imposes a strict set of rules known as the "Rules of Hooks." Understanding and adhering to these rules is critical for any React developer.
The Two Main Rules
1. Only Call Hooks at the Top Level
Do not call Hooks inside loops, conditions, or nested functions. Always use Hooks at the top level of your React function, before any early returns.
Why? React relies on the order in which Hooks are called to associate state and effects with the correct component instance across multiple renders. If a Hook is conditionally called, the order changes, leading to unpredictable behavior, lost state, and subtle bugs.
❌ Bad Practice:
function BadComponent({ condition }) {
if (condition) {
// 🚨 React will complain! Hook order changes based on condition.
const [name, setName] = useState('Alice');
}
useEffect(() => {
console.log('Component mounted or updated');
});
return <div>Hello</div>;
}
✅ Good Practice:
function GoodComponent({ condition }) {
// ✅ Hook is at the top level. Order is always consistent.
const [name, setName] = useState('Alice');
useEffect(() => {
console.log('Component mounted or updated');
});
if (!condition) {
return <div>Hello</div>;
}
return <div>Hello {name}</div>;
}
2. Only Call Hooks from React Functions
You cannot call Hooks from regular JavaScript functions. You must call them from:
- React functional components.
- Custom Hooks (which themselves are JavaScript functions whose names start with
use).
Why? This ensures that all stateful logic is clearly tied to the component lifecycle or composed into reusable Custom Hooks, keeping your code organized and maintainable.
❌ Bad Practice:
function regularJavaScriptFunction() {
// 🚨 Hooks cannot be called in standard JS functions!
const [value, setValue] = useState(0);
return value;
}
✅ Good Practice:
function useCustomHook() {
// ✅ Hooks can be called inside Custom Hooks.
const [value, setValue] = useState(0);
return value;
}
function MyComponent() {
// ✅ Hooks can be called inside React functional components.
const myValue = useCustomHook();
return <div>{myValue}</div>;
}
The ESLint Plugin: eslint-plugin-react-hooks
To enforce these rules automatically, the React team provides an ESLint plugin: eslint-plugin-react-hooks.
It includes two main rules:
react-hooks/rules-of-hooks: Verifies the two core rules (top level, React functions only). This should typically be set to"error".react-hooks/exhaustive-deps: Verifies that dependencies foruseEffect,useCallback,useMemo, etc., are correctly specified. This helps prevent stale closures. This is often set to"warn"or"error".
Installation and Setup
Most modern setups (like Create React App, Next.js, or Vite templates) include this plugin by default. If you need to set it up manually:
npm install eslint-plugin-react-hooks --save-dev
In your ESLint configuration (.eslintrc.json or similar):
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}
Summary
- Top Level Only: Never place Hooks inside conditions, loops, or nested callbacks.
- React Functions Only: Only call Hooks in React functional components or Custom Hooks.
- Use the Linter: Always configure
eslint-plugin-react-hooksto catch violations during development.