Tutorials Logic, IN info@tutorialslogic.com

React Hook called conditionally Rules of Hooks Fix

What is This Error?

The "React Hook is called conditionally" error occurs when you call a React Hook inside a conditional statement, loop, or nested function. React requires that Hooks are always called in the same order on every render "" this is one of the fundamental Rules of Hooks.

Common Causes

  • A Hook placed inside an if/else branch
  • Loop-dependent Hook calls in a for or while statement
  • A Hook hidden inside a nested function
  • Hook execution that occurs only after an early return check
  • A Hook enclosed by a try/catch block

Quick Fix (TL;DR)

Immediate Fix: Problem Hook inside condition

Immediate Fix: Problem Hook inside condition
// Wrong Problem "" Hook inside condition
function App({ isLoggedIn }) {
  if (isLoggedIn) {
    const [user, setUser] = useState(null); // Error!
  }
}

// Correct Solution "" always call Hook at top level
function App({ isLoggedIn }) {
  const [user, setUser] = useState(null); // Always called
  if (!isLoggedIn) return null; // Condition after hooks
}

Common Scenarios & Solutions

Failure: Conditional hook

Failure: Conditional hook
function UserProfile({ showDetails }) {
  if (showDetails) {
    const [details, setDetails] = useState({}); // Wrong Conditional hook!
    useEffect(() => { fetchDetails(); }, []); // Wrong Conditional hook!
  }
  return <div>Profile</div>;
}

Correction: Always call hooks at the top level

Correction: Always call hooks at the top level
function UserProfile({ showDetails }) {
  // Correct Always call hooks at the top level
  const [details, setDetails] = useState({});

  useEffect(() => {
    if (showDetails) { // Correct Condition inside the hook
      fetchDetails().then(setDetails);
    }
  }, [showDetails]);

  return <div>{showDetails && <p>{details.bio}</p>}</div>;
}

Failure: Early return

Failure: Early return
function Dashboard({ user }) {
  if (!user) return <p>Please log in</p>; // Early return

  const [data, setData] = useState([]); // Wrong Hook after return!
  useEffect(() => { fetchData(); }, []); // Wrong Hook after return!

  return <div>{data.map(item => <p>{item}</p>)}</div>;
}

Correction: All hooks BEFORE any return

Correction: All hooks BEFORE any return
function Dashboard({ user }) {
  // Correct All hooks BEFORE any return
  const [data, setData] = useState([]);

  useEffect(() => {
    if (user) fetchData().then(setData); // Condition inside effect
  }, [user]);

  if (!user) return <p>Please log in</p>; // Return AFTER hooks

  return <div>{data.map(item => <p>{item}</p>)}</div>;
}

Failure: Hook in loop

Failure: Hook in loop
function ItemList({ items }) {
  return items.map(item => {
    const [selected, setSelected] = useState(false); // Wrong Hook in loop!
    return <div onClick={() => setSelected(!selected)}>{item}</div>;
  });
}

Correction: Extract to a separate component

Correction: Extract to a separate component
// Correct Extract to a separate component
function Item({ item }) {
  const [selected, setSelected] = useState(false); // Correct Top level
  return (
    <div onClick={() => setSelected(!selected)}>
      {item} {selected ? '\u2713' : ''}
    </div>
  );
}

function ItemList({ items }) {
  return items.map(item => <Item key={item.id} item={item} />);
}

Failure: Conditional custom hook

Failure: Conditional custom hook
function App({ isAdmin }) {
  if (isAdmin) {
    const adminData = useAdminData(); // Wrong Conditional custom hook!
  }
}

Correction: Always call the hook, pass condition as parameter

Correction: Always call the hook, pass condition as parameter
// Correct Always call the hook, pass condition as parameter
function App({ isAdmin }) {
  const adminData = useAdminData(isAdmin); // Hook decides internally
}

// Inside useAdminData:
function useAdminData(isAdmin) {
  const [data, setData] = useState(null);
  useEffect(() => {
    if (isAdmin) fetchAdminData().then(setData); // Condition inside
  }, [isAdmin]);
  return data;
}

Best Practices to Avoid This Error

  • Always call Hooks at the top level - Before any conditions, loops, or returns
  • Put conditions inside Hooks - Not the other way around
  • Extract components for loops - Each list item should be its own component
  • Move early returns after all Hooks - All hooks must run before any return
  • Use eslint-plugin-react-hooks - Automatically catches Rules of Hooks violations
  • Pass conditions as parameters - Let custom hooks handle conditions internally
  • Only call Hooks in React functions - Not in regular JS functions or class components

Hook Order Is the Component State Address

React associates each Hook call with its position in the component call sequence. If a condition, loop, early return, nested function, or try block changes that sequence between renders, later Hook state is read from the wrong position. The rule protects state identity; it is not only a lint preference.

Keep Hook calls at the top level of a component or custom Hook. Put the condition inside the effect, event handler, memoized calculation, or returned JSX. An early return is safe only after every Hook that participates in the component has been called in the same order.

Refactor Optional Behavior Without Optional Hook Calls

For an optional subscription, call the effect every render and return without subscribing when disabled. For separate feature modes with different state, render separate child components so each component has its own stable Hook sequence. A custom Hook follows the same rules as a component and must not hide conditional calls.

Run the Hooks lint rules in development and test transitions across every condition that previously changed the call order. Strict Mode can expose unsafe effects, but the definitive correction is a stable call sequence with complete cleanup.

Before you move on

React Hook called conditionally Rules of Hooks Fix Mastery Check

5 checks
  • Move Hooks out of nested functions and into the component or custom Hook body.
  • Place every Hook before an early return so call order remains stable.
  • Keep Hook calls outside try/catch blocks and handle failures inside effects or event logic.
  • Move every Hook before early returns, then put conditional behavior inside the Hook callback or an extracted component.
  • Run the Hooks ESLint rules in CI so a later refactor cannot silently reintroduce order-dependent calls.

Try this next

React JS Hook Called Conditionally Repair Drills

0 of 2 completed

  1. Toggle a prop across renders so an early return changes the old hook order, then move hooks above the return and rerun the component test. Every render of one component must call hooks in the same order.
  2. Keep the hook call unconditional while moving the condition inside its callback, or extract a child component whose entire lifecycle is conditional. Run the Hooks ESLint rules after the refactor to catch a future order change.

React JS Questions Learners Ask

React associates hook state by call order. If one render calls useState, then useEffect, and the next render skips the useState because an if branch changed, every later hook shifts position.

Usually you still call the hook, then make the work conditional. For useEffect, write the guard inside the effect: if (!enabled) return; then run the subscription or fetch. For a custom hook, pass enabled as an option and let the hook decide whether to do anything.

Do not call hooks directly inside map, for, or while in the same component. The number and order of calls can change when the list length changes or items are filtered. Instead, render a child component for each item and put the hook inside that child component.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.