React has strict rules for using Hooks, and one of the most important rules is:
Hooks must be called at the top level of a component and not inside loops, conditions, or nested functions.
This ensures that React preserves the order of Hooks across renders.
If you call Hooks inside a loop, condition, or nested function, React loses track of their order across renders, causing unpredictable behavior.
function Example({ condition }) {
if (condition) {
// WRONG: Hook inside a condition
const [count, setCount] = useState(0);
}
return <p>Count: {count}</p>; // count is not defined
}
Fix : Always declare Hooks at the top level:
function Example({ condition }) {
const [count, setCount] = useState(0); // Always at the top
return condition ? <p>Count: {count}</p> : <p>No count</p>;
}
Instead of conditionally using a Hook, conditionally render components that use Hooks.
function Counter() {
const [count, setCount] = useState(0);
return <p>Count: {count}</p>;
}
function Example({ condition }) {
return condition ? <Counter /> : <p>No counter</p>;
}
This way, Counter
always initializes its Hooks properly.
If you need multiple instances of a Hook, store data in an array and map over it.
function WrongComponent() {
const items = [1, 2, 3];
items.forEach(item => {
// ? WRONG: Hooks inside a loop
const [count, setCount] = useState(0);
});
return <p>Invalid Hook usage!</p>;
}
function Counter({ id }) {
const [count, setCount] = useState(0);
return (
<p>
Item {id}: {count}
</p>
);
}
function CorrectComponent() {
const items = [1, 2, 3];
return (
<div>
{items.map(item => (
<Counter key={item} id={item} />
))}
</div>
);
}
* Each Counter
component has its own useState
, ensuring consistent behavior.
Don't Do This | Do This Instead |
---|---|
Hooks inside loops | Use .map() to render components |
Hooks inside conditions | Always declare Hooks at the top level |
Hooks inside nested functions | Define the Hook outside the function |