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:
-
Calling Hooks Inside Loops, Conditions, or Nested Functions:
- Diagnosis: Review your component’s render logic. Look for
ifstatements,fororwhileloops, 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.
- Diagnosis: Review your component’s render logic. Look for
-
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
useand 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
useas hook-related and applies its rules.
-
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.
-
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
useprefix is a convention enforced by React’s linter and build tools to identify hook calls.
- Diagnosis: You’ve created a custom hook but forgot to prefix its name with
-
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.
- 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,
-
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
reactandreact-domto 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.