search
React star Featured

Fix: Invalid React hook call. Hooks can only be called inside of the body of a function component

Learn how to fix the 'Invalid hook call' error in React. This guide covers all causes, solutions, and best practices for proper React hook usage with step-by-step examples.

person By Gautam Sharma
calendar_today January 1, 2026
schedule 11 min read
React Hooks Error Frontend Development JavaScript

The ‘Invalid hook call. Hooks can only be called inside of the body of a function component’ error is one of the most common React errors developers encounter. This error occurs when React hooks are used incorrectly, violating React’s fundamental rules for hook usage.

This comprehensive guide explains what causes this error, why it happens, and provides multiple solutions to fix it in your React applications with clean code examples and directory structure.


What is the Invalid Hook Call Error?

React hooks are special functions that allow you to use state and other React features in function components. The invalid hook call error occurs when hooks are used outside of function components or when there are multiple copies of React in your project, causing conflicts in the React internals.

Common Error Messages:

  • Invalid hook call. Hooks can only be called inside of the body of a function component
  • Invalid hook call. Hooks can only be called inside of the body of a function component or a custom Hook
  • Minified React error #321

Understanding the Problem

React hooks must follow specific rules:

  1. Hooks can only be called inside React function components
  2. Hooks can only be called at the top level of a function
  3. Hooks cannot be called inside loops, conditions, or nested functions

Typical React Project Structure:

my-react-app/
├── package.json
├── src/
│   ├── App.jsx
│   ├── components/
│   │   ├── Header.jsx
│   │   └── UserProfile.jsx
│   ├── hooks/
│   │   └── useCustomHook.js
│   └── utils/
│       └── helpers.js

Solution 1: Move Hooks to Function Components

The most common cause is calling hooks outside of function components.

❌ Incorrect Usage:

// utils/helpers.js
import { useState } from 'react';

// ❌ This will cause an error
const [count, setCount] = useState(0);

function incrementCount() {
  setCount(count + 1);
}

export { incrementCount };

✅ Correct Usage:

// components/UserProfile.jsx
import { useState } from 'react';

function UserProfile() {
  const [count, setCount] = useState(0);

  function incrementCount() {
    setCount(count + 1);
  }

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={incrementCount}>Increment</button>
    </div>
  );
}

export default UserProfile;

Solution 2: Create Custom Hooks for Reusable Logic

When you need to share hook logic, create custom hooks instead of calling hooks outside components.

❌ Incorrect Usage:

// utils/api.js
import { useState, useEffect } from 'react';

// ❌ This will cause an error
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);

function fetchData() {
  // API call logic
}

export { data, loading, fetchData };

✅ Correct Usage:

// hooks/useApi.js
import { useState, useEffect } from 'react';

function useApi(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        const response = await fetch(url);
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
}

export default useApi;

Using the Custom Hook:

// components/DataComponent.jsx
import useApi from '../hooks/useApi';

function DataComponent() {
  const { data, loading, error } = useApi('https://api.example.com/data');

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}

export default DataComponent;

Solution 3: Fix Multiple React Versions

Having multiple copies of React in your project can cause hook errors.

Step 1: Check for Multiple React Versions

npm ls react

Step 2: Resolve Version Conflicts

# Remove node_modules and reinstall
rm -rf node_modules package-lock.json
npm install

# Or use npm dedupe to remove duplicates
npm dedupe

Step 3: Use npm ls to Verify

npm ls react --depth=0

Solution 4: Proper Hook Call Positioning

Hooks must be called at the top level of a function component, not inside conditions or loops.

❌ Incorrect Usage:

// components/ConditionalComponent.jsx
import { useState, useEffect } from 'react';

function ConditionalComponent({ showFeature }) {
  if (showFeature) {
    // ❌ Hook called inside condition
    const [data, setData] = useState(null);
  }

  return <div>Component content</div>;
}

✅ Correct Usage:

// components/ConditionalComponent.jsx
import { useState, useEffect } from 'react';

function ConditionalComponent({ showFeature }) {
  // ✅ Hook called at top level
  const [data, setData] = useState(null);

  useEffect(() => {
    if (showFeature) {
      // Logic inside effect
      setData('Some data');
    }
  }, [showFeature]);

  return (
    <div>
      {showFeature && <p>{data}</p>}
    </div>
  );
}

Solution 5: Fix Hooks in Loops

Hooks cannot be called inside loops or array methods.

❌ Incorrect Usage:

// components/ListComponent.jsx
import { useState } from 'react';

function ListComponent({ items }) {
  return (
    <div>
      {items.map((item, index) => {
        // ❌ Hook called inside map
        const [isSelected, setIsSelected] = useState(false);
        
        return (
          <div key={item.id}>
            <input
              type="checkbox"
              checked={isSelected}
              onChange={() => setIsSelected(!isSelected)}
            />
            {item.name}
          </div>
        );
      })}
    </div>
  );
}

✅ Correct Usage:

// components/ListComponent.jsx
import { useState } from 'react';

function ListComponent({ items }) {
  // ✅ Single hook for all items
  const [selectedItems, setSelectedItems] = useState({});

  const toggleItem = (itemId) => {
    setSelectedItems(prev => ({
      ...prev,
      [itemId]: !prev[itemId]
    }));
  };

  return (
    <div>
      {items.map(item => (
        <div key={item.id}>
          <input
            type="checkbox"
            checked={!!selectedItems[item.id]}
            onChange={() => toggleItem(item.id)}
          />
          {item.name}
        </div>
      ))}
    </div>
  );
}

Solution 6: Fix Hooks in Nested Functions

Hooks cannot be called inside nested functions within components.

❌ Incorrect Usage:

// components/NestedComponent.jsx
import { useState } from 'react';

function NestedComponent() {
  function handleClick() {
    // ❌ Hook called inside nested function
    const [count, setCount] = useState(0);
    setCount(count + 1);
  }

  return <button onClick={handleClick}>Click me</button>;
}

✅ Correct Usage:

// components/NestedComponent.jsx
import { useState } from 'react';

function NestedComponent() {
  const [count, setCount] = useState(0);

  function handleClick() {
    // ✅ Hook used inside nested function
    setCount(count + 1);
  }

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Click me</button>
    </div>
  );
}

Solution 7: Fix Class Component Issues

Hooks cannot be used in class components.

❌ Incorrect Usage:

// components/ClassComponent.jsx
import { useState } from 'react';

class ClassComponent extends React.Component {
  constructor(props) {
    super(props);
    // ❌ Hooks cannot be used in class components
    const [count, setCount] = useState(0);
  }

  render() {
    return <div>Count: {this.state.count}</div>;
  }
}

✅ Correct Usage:

// components/FunctionComponent.jsx
import { useState } from 'react';

function FunctionComponent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

export default FunctionComponent;

Solution 8: Fix Import Issues

Ensure React is properly imported when using hooks.

❌ Incorrect Usage:

// components/NoReactImport.jsx
// ❌ React not imported
import { useState } from 'react';

function NoReactImport() {
  const [count, setCount] = useState(0);
  return <div>Count: {count}</div>;
}

✅ Correct Usage:

// components/ProperImport.jsx
import React from 'react'; // Required for JSX
import { useState } from 'react';

function ProperImport() {
  const [count, setCount] = useState(0);
  return <div>Count: {count}</div>;
}

export default ProperImport;

Solution 9: Webpack Configuration Fix

For custom Webpack setups, ensure proper configuration to avoid multiple React instances.

webpack.config.js:

module.exports = {
  resolve: {
    alias: {
      'react': path.resolve(__dirname, './node_modules/react'),
      'react-dom': path.resolve(__dirname, './node_modules/react-dom'),
    }
  },
  // Other configuration...
};

Solution 10: Package Manager Specific Fixes

For Yarn:

# Check for multiple React versions
yarn why react

# Force single version
yarn add react@latest react-dom@latest

For pnpm:

# pnpm naturally avoids duplicates, but verify
pnpm list react

Complete Project Structure After Fix

my-react-app/
├── package.json
├── package-lock.json
├── node_modules/
├── public/
│   └── index.html
├── src/
│   ├── App.jsx
│   ├── index.js
│   ├── components/
│   │   ├── UserProfile.jsx
│   │   ├── DataComponent.jsx
│   │   └── ListComponent.jsx
│   ├── hooks/
│   │   └── useApi.js
│   └── utils/
│       └── helpers.js

Working Code Examples

Custom Hook Example:

// hooks/useLocalStorage.js
import { useState, useEffect } from 'react';

function useLocalStorage(key, initialValue) {
  // Get from local storage or use initial value
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  // Update local storage when value changes
  useEffect(() => {
    try {
      window.localStorage.setItem(key, JSON.stringify(storedValue));
    } catch (error) {
      console.error(error);
    }
  }, [key, storedValue]);

  return [storedValue, setStoredValue];
}

export default useLocalStorage;

Component Using Custom Hook:

// components/Settings.jsx
import useLocalStorage from '../hooks/useLocalStorage';

function Settings() {
  const [theme, setTheme] = useLocalStorage('theme', 'light');

  return (
    <div>
      <p>Current theme: {theme}</p>
      <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
        Toggle Theme
      </button>
    </div>
  );
}

export default Settings;

Best Practices to Prevent Hook Errors

1. Always Use ESLint Plugin

Install and configure the React hooks ESLint plugin:

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

2. Follow the Rules of Hooks

  • Only call hooks at the top level
  • Only call hooks in React functions or custom hooks
  • Use the ESLint plugin to catch violations

3. Use Custom Hooks for Complex Logic

// ✅ Good: Custom hook for complex logic
function useComplexLogic() {
  const [state, setState] = useState(initialValue);
  
  useEffect(() => {
    // Complex logic here
  }, []);

  return { state, setState };
}

4. Keep Hooks at the Top

// ✅ Good: All hooks at top level
function MyComponent() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('');
  
  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

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

Debugging Steps

Step 1: Check Hook Usage

# Install ESLint plugin to catch violations
npm install eslint-plugin-react-hooks --save-dev

Step 2: Verify React Version

npm list react react-dom

Step 3: Check for Multiple Instances

npm ls react --depth=10

Step 4: Clear Cache and Reinstall

rm -rf node_modules package-lock.json
npm install

Common Mistakes to Avoid

1. Hooks in Conditions

// ❌ Don't do this
if (condition) {
  const [state, setState] = useState(initialValue);
}

2. Hooks in Loops

// ❌ Don't do this
items.map(item => {
  const [state, setState] = useState(item.value);
  return <div>{state}</div>;
});

3. Hooks in Event Handlers

// ❌ Don't do this
function handleClick() {
  const [state, setState] = useState(initialValue);
}

4. Hooks in Class Components

// ❌ Don't do this
class MyComponent extends React.Component {
  const [state, setState] = useState(initialValue);
}

Performance Considerations

1. Memoization

Use useMemo and useCallback to optimize performance:

const expensiveValue = useMemo(() => {
  // Expensive computation
  return computeExpensiveValue(props.data);
}, [props.data]);

const handleClick = useCallback(() => {
  // Handle click
}, []);

2. Avoid Unnecessary Re-renders

// Use React.memo for components
const MyComponent = React.memo(({ data }) => {
  return <div>{data}</div>;
});

Security Considerations

1. Validate Hook Inputs

Always validate inputs to custom hooks:

function useApi(url) {
  if (!url || typeof url !== 'string') {
    throw new Error('URL must be a valid string');
  }
  // Hook logic...
}

2. Sanitize Data

Sanitize data retrieved through hooks:

function useSafeData(data) {
  const [safeData, setSafeData] = useState('');
  
  useEffect(() => {
    // Sanitize data before setting
    setSafeData(sanitize(data));
  }, [data]);
}

Testing Hook Usage

1. Unit Tests

// Using React Testing Library
import { renderHook, act } from '@testing-library/react';
import useCounter from '../hooks/useCounter';

test('should increment counter', () => {
  const { result } = renderHook(() => useCounter());
  
  act(() => {
    result.current.increment();
  });
  
  expect(result.current.count).toBe(1);
});

2. Component Tests

// Test components that use hooks
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from '../components/Counter';

test('should increment counter', () => {
  render(<Counter />);
  
  fireEvent.click(screen.getByText('Increment'));
  
  expect(screen.getByText('Count: 1')).toBeInTheDocument();
});

Alternative Solutions

1. Convert to Class Component

If hooks are causing persistent issues:

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

2. Use Higher-Order Components

For shared logic without hooks:

function withCounter(WrappedComponent) {
  return function EnhancedComponent(props) {
    const [count, setCount] = useState(0);
    return <WrappedComponent count={count} setCount={setCount} {...props} />;
  };
}

Conclusion

The ‘Invalid hook call’ error occurs when React hooks are used outside of function components or when there are multiple React instances in your project. By following React’s rules for hooks—calling them only at the top level of function components and custom hooks—you can avoid this error entirely.

The key solutions include moving hooks to proper locations, creating custom hooks for reusable logic, resolving multiple React versions, and following best practices for hook usage. With these solutions implemented and proper testing, your React applications will use hooks correctly without encountering invalid hook call errors.

Remember to use the React Hooks ESLint plugin to catch violations automatically and maintain consistent React versions across your project dependencies.

Gautam Sharma

About Gautam Sharma

Full-stack developer and tech blogger sharing coding tutorials and best practices

Related Articles

React

Fix: Cannot read properties of undefined (reading 'map') in React - Complete Guide

Learn how to fix the 'Cannot read properties of undefined (reading map)' error in React. This guide covers causes, solutions, and best practices for handling arrays safely.

January 1, 2026
React

Getting Started with React Hooks in 2025

Learn how to use React Hooks effectively in your modern React applications with practical examples and best practices.

December 15, 2024
React

Fix: Module not found: Can't resolve 'react/jsx-runtime' - Complete Solution Guide

Learn how to fix the 'Module not found: Can't resolve react/jsx-runtime' error in React projects. This guide covers causes, solutions, and prevention strategies with step-by-step instructions.

January 1, 2026