🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

How to Fix React Hook useState is Called Conditionally Error

⏱️4 min read  ·  812 words

The error React Hook “useState” is called conditionally. React Hooks must be called in the exact same order in every component render is one of the most common React mistakes. It violates the Rules of Hooks. Here’s why it happens and how to fix every variant.

Why This Rule Exists

React tracks hooks by their call order, not by name. On every render, React expects the same hooks in the same sequence. If a hook is inside a condition, it might run on one render but not the next — breaking React’s internal tracking and causing state to be assigned to the wrong hook.

The Broken Code

// 🐛 Hook inside a condition — VIOLATES rules of hooks
function Profile({ userId }) {
  if (!userId) {
    return <div>No user</div>;  // early return BEFORE hooks
  }

  const [user, setUser] = useState(null);  // ❌ conditionally called
  // If userId is falsy, this hook is skipped — breaks React's tracking

  return <div>{user?.name}</div>;
}

Fix 1: Call Hooks First, Condition Later

// ✅ All hooks at the top, unconditionally
function Profile({ userId }) {
  const [user, setUser] = useState(null);  // always called

  // Conditional logic AFTER all hooks
  if (!userId) {
    return <div>No user</div>;
  }

  return <div>{user?.name}</div>;
}

Fix 2: Hook Inside a Condition Block

// 🐛 useEffect inside an if
function Component({ isActive }) {
  if (isActive) {
    useEffect(() => {          // ❌ conditional hook
      console.log('active');
    }, []);
  }
}

// ✅ Move the condition INSIDE the hook
function Component({ isActive }) {
  useEffect(() => {
    if (isActive) {            // condition inside the hook
      console.log('active');
    }
  }, [isActive]);
}

Fix 3: Hook Inside a Loop

// 🐛 Hook in a loop — order changes if items.length changes
function List({ items }) {
  return items.map(item => {
    const [selected, setSelected] = useState(false);  // ❌ in a loop
    return <Item key={item.id} />;
  });
}

// ✅ Extract into a child component — each has its own hooks
function List({ items }) {
  return items.map(item => <Item key={item.id} item={item} />);
}

function Item({ item }) {
  const [selected, setSelected] = useState(false);  // ✅ top level of component
  return <div onClick={() => setSelected(!selected)}>{item.name}</div>;
}

Fix 4: Hook After an Early Return

// 🐛 Hook after a conditional return
function Widget({ data }) {
  const [count, setCount] = useState(0);

  if (!data) return null;      // early return

  const [name, setName] = useState('');  // ❌ never reached when !data

  return <div>{name}: {count}</div>;
}

// ✅ ALL hooks before ANY conditional return
function Widget({ data }) {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('');  // moved up

  if (!data) return null;      // early return AFTER all hooks

  return <div>{name}: {count}</div>;
}

The Two Rules of Hooks

  1. Only call hooks at the top level — never inside conditions, loops, or nested functions. Always in the same order.
  2. Only call hooks from React functions — React components or custom hooks (functions starting with use). Not from regular JavaScript functions.

Handling Conditional Logic Correctly

function Dashboard({ user, showStats }) {
  // ✅ Always call all hooks unconditionally
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // Conditions go INSIDE the hook, not around it
    if (showStats) {
      fetchStats().then(setData).finally(() => setLoading(false));
    } else {
      setLoading(false);
    }
  }, [showStats]);

  // Conditional RENDERING is fine — it's not a conditional hook
  if (!user) return <Login />;
  if (loading) return <Spinner />;

  return showStats ? <Stats data={data} /> : <Overview />;
}

Enforce the Rules with ESLint

npm install --save-dev eslint-plugin-react-hooks

// .eslintrc — catches these errors before runtime
{
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn"
  }
}

Frequently Asked Questions

Q: Why can’t hooks be conditional? It seems arbitrary.
A: React identifies hooks by their call order (first useState, second useState, etc.), not by name. If a hook is sometimes skipped, the order shifts and React assigns state to the wrong hook. Consistent order is what makes hooks work.

Q: Can I use conditions inside a hook?
A: Yes! Conditions INSIDE a hook (inside the useEffect callback, for example) are fine. The rule only forbids conditionally CALLING the hook itself.

Q: How do I conditionally run an effect?
A: Put the condition inside the effect body and include the condition variable in the dependency array: useEffect(() => { if (cond) {...} }, [cond]).

Q: What about conditional rendering — is that allowed?
A: Yes. Conditional rendering (returning different JSX based on state) is completely fine, as long as all hooks are called before any conditional return. Rendering conditions aren’t hook conditions.

Q: Can I call a custom hook conditionally?
A: No — custom hooks follow the same rules since they call hooks internally. Call them unconditionally at the top level, and put conditions inside them.

Conclusion

The “React Hook is called conditionally” error means a hook is inside a condition, loop, or after an early return. The fix is always the same: call ALL hooks unconditionally at the top of your component, before any conditional returns or logic. Put conditions INSIDE hooks (in the effect body), not around them. Install eslint-plugin-react-hooks to catch these mistakes automatically before they reach runtime — it’s the single best safeguard against hook rule violations.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা