How to fix React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render. in React
This ESLint error occurs when useEffect or other React Hooks are called conditionally, inside loops, or after early returns. React requires all Hooks to be called in the same order on every render to maintain internal state consistency.
Quick fix
Fixes react-hooks/rules-of-hooks
function MyComponent({ isEnabled }) {
if (!isEnabled) {
return <div>Disabled</div>;
}
// ❌ Error: Hook called after early return
useEffect(() => {
console.log('Component mounted');
}, []);
return <div>Enabled</div>;
}- Move the Hook call to the top level of your component
- Move conditional logic inside the Hook
- Extract Hooks to a separate child component
- Use dependency arrays for conditional execution
function MyComponent({ isEnabled }) {if (!isEnabled) {return <div>Disabled</div>;}// ❌ Error: Hook called after early returnuseEffect(() => {console.log('Component mounted');}, []);return <div>Enabled</div>;}React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render.
What this error means
This error is triggered by the eslint-plugin-react-hooks linting rule that enforces React's fundamental Rules of Hooks. The error appears when you place a Hook call (like useEffect, useState, useCallback, etc.) inside a conditional statement, loop, or nested function, or after an early return statement. React Hooks rely on call order to maintain their internal state between renders. Each Hook call is tracked by React using a numeric index based on the order in which Hooks are called. If the order changes between renders—such as when a Hook is conditionally called—React loses track of which state belongs to which Hook, causing bugs and unexpected behavior. The eslint-plugin-react-hooks package, developed by the React team, automatically detects these violations during development to prevent runtime bugs that would be difficult to debug.
Typical symptoms
- ESLint error in your IDE or terminal: "React Hook is called conditionally"
- Build or lint process fails with react-hooks/rules-of-hooks violation
- Component behavior becomes unpredictable when conditions change between renders
- State values get mixed up between different Hook calls
- React DevTools shows incorrect Hook values or state
Common causes
- Placing useEffect or other Hooks inside if/else statements or ternary operators
- Calling Hooks after an early return statement in a component
- Using Hooks inside loops (for, while, forEach, map)
- Placing Hooks inside nested functions or callbacks
- Conditionally rendering logic that includes Hook calls without moving Hooks to a child component
- Using Hooks inside try/catch blocks or switch statements
How to fix "React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render."
The simplest fix is to ensure all Hooks are called at the top of your component function, before any conditional logic or early returns.
Before (incorrect):
function MyComponent({ isEnabled }) {
if (!isEnabled) {
return <div>Disabled</div>;
}
// ❌ Error: Hook called after early return
useEffect(() => {
console.log('Component mounted');
}, []);
return <div>Enabled</div>;
}After (correct):
function MyComponent({ isEnabled }) {
// ✅ Hook called at top level
useEffect(() => {
console.log('Component mounted');
}, []);
if (!isEnabled) {
return <div>Disabled</div>;
}
return <div>Enabled</div>;
}Instead of conditionally calling the Hook, call it unconditionally but add your condition inside the Hook's callback. This ensures the Hook runs on every render while the logic executes conditionally.
Before (incorrect):
function MyComponent({ shouldFetch, userId }) {
// ❌ Error: Hook called conditionally
if (shouldFetch) {
useEffect(() => {
fetchUserData(userId);
}, [userId]);
}
return <div>Content</div>;
}After (correct):
function MyComponent({ shouldFetch, userId }) {
// ✅ Hook always called, condition inside
useEffect(() => {
if (!shouldFetch) {
return; // Early return inside the effect
}
fetchUserData(userId);
}, [shouldFetch, userId]);
return <div>Content</div>;
}When you need to conditionally use Hooks based on props or state, create a child component that contains the Hook calls, then conditionally render that component.
Before (incorrect):
function Dashboard({ user }) {
if (user.isAdmin) {
// ❌ Error: Hook called conditionally
useEffect(() => {
loadAdminData();
}, []);
}
return <div>Dashboard</div>;
}After (correct):
function AdminEffects() {
// ✅ Hook always called in this component
useEffect(() => {
loadAdminData();
}, []);
return null; // This component only runs effects
}
function Dashboard({ user }) {
return (
<div>
{user.isAdmin && <AdminEffects />}
Dashboard
</div>
);
}For effects that should only run when certain values change, rely on the dependency array instead of conditional Hook calls.
Before (incorrect):
function SearchResults({ query }) {
// ❌ Error: Hook called conditionally
if (query.length > 0) {
useEffect(() => {
searchAPI(query);
}, [query]);
}
return <div>Results</div>;
}After (correct):
function SearchResults({ query }) {
// ✅ Hook always called, dependencies control execution
useEffect(() => {
if (query.length === 0) {
return;
}
searchAPI(query);
}, [query]); // Effect re-runs when query changes
return <div>Results</div>;
}Advanced notes
Why Hook Order Matters:
React doesn't use names or IDs to track Hooks—it uses the order in which they're called. React maintains an internal array of Hook state, and each call to a Hook like useState or useEffect corresponds to an index in that array. When Hook calls are conditional, the indices shift between renders, causing React to return the wrong state values.
ESLint Configuration:
The react-hooks/rules-of-hooks rule is part of eslint-plugin-react-hooks. Ensure your ESLint config includes:
{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}Custom Hooks Follow the Same Rules:
Custom Hooks (functions starting with "use") must also follow these rules. Any Hook calls inside your custom Hooks must be unconditional:
// ❌ Incorrect
function useCustomHook(shouldRun) {
if (shouldRun) {
useEffect(() => { /* ... */ }, []);
}
}
// ✅ Correct
function useCustomHook(shouldRun) {
useEffect(() => {
if (!shouldRun) return;
// ... effect logic
}, [shouldRun]);
}False Positives:
Occasionally, ESLint may flag code that appears conditional but actually isn't (e.g., after complex control flow). In rare cases where you're certain the code is correct, you can disable the rule for that line:
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => { /* ... */ }, []);However, this should be a last resort—most of the time, the linter is correctly identifying a real issue.
Frequently asked questions
- What does "react-hooks/rules-of-hooks" mean?
- This error is triggered by the eslint-plugin-react-hooks linting rule that enforces React's fundamental Rules of Hooks. ) inside a conditional statement, loop, or nested function, or after an early return statement. React Hooks rely on call order to maintain their internal state between renders.
- How do I fix "react-hooks/rules-of-hooks"?
- This ESLint error occurs when useEffect or other React Hooks are called conditionally, inside loops, or after early returns. React requires all Hooks to be called in the same order on every render to maintain internal state consistency.
- What causes "react-hooks/rules-of-hooks"?
- Placing useEffect or other Hooks inside if/else statements or ternary operators Calling Hooks after an early return statement in a component Using Hooks inside loops (for, while, forEach, map)
- Is the "react-hooks/rules-of-hooks" error serious?
- Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.