React Hooks can only be called inside function components because the hook system relies on the component’s render cycle to maintain state and execution order.

Here’s what’s actually broken: A function component is trying to call a hook (like useState, useEffect, useContext, etc.) outside of its own execution scope, or a hook is being called conditionally in a way that breaks React’s internal tracking. This leads to an inconsistent state between what React expects to find in its internal "memoized" state for that component and what’s actually being executed, causing the "Invalid hook call" error.

Common Causes and Fixes:

  1. Calling Hooks Inside Loops, Conditions, or Nested Functions:

    • Diagnosis: Review your component’s render logic. Look for if statements, for or while loops, or any nested functions within the component’s render body where a hook call might reside.
    • Fix: Ensure all hook calls are at the top level of your function component, directly inside the component’s body, not inside any conditional logic or loops.
      // Incorrect: Hook inside an if statement
      function MyComponent(props) {
        if (props.show) {
          const [value, setValue] = useState(0); // ERROR
        }
        // ...
      }
      
      // Correct: Hook at the top level
      function MyComponent(props) {
        const [value, setValue] = useState(0); // OK
        if (props.show) {
          // ... use value and setValue
        }
        // ...
      }
      
    • Why it works: React maintains a list of hooks for each component instance. It expects this list to be called in the exact same order on every render. Conditional calls break this order.
  2. Calling Hooks Inside Regular JavaScript Functions (Not Components):

    • Diagnosis: Search your codebase for hook calls that are not directly within a function component or a custom hook. This often happens when trying to reuse logic.
    • Fix: If you need to reuse stateful logic, extract it into a custom hook. A custom hook is just a JavaScript function whose name starts with use and that calls other hooks.
      // Incorrect: Hook called in a non-component function
      function fetchData(url) {
        const [data, setData] = useState(null); // ERROR
        useEffect(() => {
          fetch(url).then(res => res.json()).then(setData);
        }, [url]);
        return data;
      }
      
      // Correct: Custom hook
      function useFetch(url) {
        const [data, setData] = useState(null);
        useEffect(() => {
          fetch(url).then(res => res.json()).then(setData);
        }, [url]);
        return data;
      }
      
      function MyComponent() {
        const data = useFetch('/api/data'); // OK
        // ...
      }
      
    • Why it works: Custom hooks are the designated way to share stateful logic. React recognizes functions starting with use as hook-related and applies its rules.
  3. Calling Hooks Inside Class Components:

    • Diagnosis: You’re likely mixing class component syntax with hook usage.
    • Fix: Hooks are only for function components. If you’re using a class component, you cannot use hooks directly within it. You’ll need to convert the class component to a function component or use Higher-Order Components (HOCs) or Render Props to pass data from hook-using components.
      // Incorrect: Hook inside a class component
      class MyClassComponent extends React.Component {
        render() {
          const [count, setCount] = useState(0); // ERROR
          return <div>{count}</div>;
        }
      }
      
      // Correct: Convert to function component
      function MyFunctionComponent() {
        const [count, setCount] = useState(0);
        return <div>{count}</div>;
      }
      
    • Why it works: Class components manage their own state and lifecycle methods. Hooks are a different paradigm designed for function components.
  4. Incorrectly Named Custom Hooks:

    • Diagnosis: You’ve created a custom hook but forgot to prefix its name with use.
    • Fix: Rename your custom hook function to start with use.
      // Incorrect: Not a valid hook name
      function fetchData(url) { /* ... */ }
      
      // Correct: Valid hook name
      function useFetch(url) { /* ... */ }
      
    • Why it works: The use prefix is a convention enforced by React’s linter and build tools to identify hook calls.
  5. Multiple Calls to the Same Hook in Different Render Paths:

    • Diagnosis: Even if hooks are at the top level, if they are conditionally executed based on props or state, and that condition changes, React can get confused. For example, if (condition) { const [a, setA] = useState(0); } else { const [b, setB] = useState(0); } is problematic.
    • Fix: Ensure that the number and order of hook calls are identical on every render. If you need conditional state, initialize it always and then conditionally use it.
      // Incorrect: Conditional hook initialization
      function MyComponent({ type }) {
        if (type === 'A') {
          const [value, setValue] = useState(null); // ERROR if type changes
        } else {
          const [value, setValue] = useState(null); // ERROR if type changes
        }
        // ...
      }
      
      // Correct: Initialize always, use conditionally
      function MyComponent({ type }) {
        const [value, setValue] = useState(null); // Initialized always
      
        useEffect(() => {
          if (type === 'A') {
            // Fetch A-specific data
          } else {
            // Fetch B-specific data
          }
        }, [type]);
      
        // ... use value and setValue
      }
      
    • Why it works: This guarantees that the hook list is stable across renders, preventing React from losing track of which state belongs to which hook.
  6. Version Mismatches or Incompatible Libraries:

    • Diagnosis: You might be using an older version of React that doesn’t support hooks, or a third-party library that’s not hook-compatible or has a different hook implementation.
    • Fix: Ensure you are using React 16.8 or later. Check the documentation for any third-party libraries you’re using to ensure compatibility with React Hooks. Update react and react-dom to the latest stable version.
      npm install react@latest react-dom@latest
      # or
      yarn add react@latest react-dom@latest
      
    • Why it works: Hooks were introduced in React 16.8. Older versions simply don’t have the necessary internal mechanisms to support them.

After fixing these, the next error you might encounter is related to missing dependencies in useEffect or useCallback, or potentially infinite re-renders if state updates are not handled correctly.

Want structured learning?

Take the full React course →