No articles found
Try different keywords or browse our categories
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.
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 componentInvalid hook call. Hooks can only be called inside of the body of a function component or a custom HookMinified React error #321
Understanding the Problem
React hooks must follow specific rules:
- Hooks can only be called inside React function components
- Hooks can only be called at the top level of a function
- 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.
Related Articles
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.
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.
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.