This error occurs when trying to use React Hooks like useState or useEffect inside class components. Hooks only work in function components and custom hooks, enforcing one of the fundamental Rules of Hooks that React uses to maintain state consistency.
React Hooks were introduced in React 16.8 as a way to use state and lifecycle features in function components. However, they are fundamentally incompatible with class components due to how they rely on the order and context of function calls to maintain state across renders. When you attempt to call a hook like useState, useEffect, or useContext inside a class component's methods (render, componentDidMount, etc.), React throws this error because hooks depend on React's internal fiber architecture in ways that class components don't support. Hooks track state by the order they're called during each render, using a linked list maintained by React's reconciliation algorithm. This mechanism simply doesn't exist in the context of class component methods. The error serves as a safeguard enforcing the Rules of Hooks - one of React's core architectural principles. It prevents developers from mixing incompatible patterns that would lead to unpredictable behavior, state corruption, or crashes. Understanding this constraint is essential because it reflects the deliberate design decision to keep hooks and classes separate, encouraging modern codebases to migrate toward function components for new development.
Check the error stack trace to find which class component and which method is attempting to use a hook:
Error: Invalid hook call. Hooks can only be called inside
of the body of a function component.
at UserProfile.render (UserProfile.js:15)Look through your class component for any of these hook calls:
- useState
- useEffect
- useContext
- useReducer
- useCallback
- useMemo
- useRef
- Any custom hooks (functions starting with "use")
Example problematic code:
class UserProfile extends React.Component {
render() {
const [name, setName] = useState(''); // ❌ Can't use hooks here
return <div>{name}</div>;
}
}The recommended solution is to migrate your class component to a function component:
Before (class component with error):
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
componentDidMount() {
// ❌ Can't use hooks in lifecycle methods
const [value, setValue] = useState(0);
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Increment
</button>
</div>
);
}
}After (function component with hooks):
function Counter() {
const [count, setCount] = useState(0); // ✅ Hooks work in function components
useEffect(() => {
console.log('Component mounted');
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}Convert class component state management to useState:
Class component pattern:
class UserForm extends React.Component {
state = {
name: '',
email: ''
};
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
}Function component with hooks:
function UserForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const handleChange = (e) => {
if (e.target.name === 'name') setName(e.target.value);
if (e.target.name === 'email') setEmail(e.target.value);
};
// Or use a single state object:
const [formData, setFormData] = useState({ name: '', email: '' });
const handleChange2 = (e) => {
setFormData(prev => ({
...prev,
[e.target.name]: e.target.value
}));
};
}Convert class lifecycle methods to useEffect hook:
Class component lifecycle:
class DataFetcher extends React.Component {
componentDidMount() {
this.fetchData();
}
componentDidUpdate(prevProps) {
if (prevProps.userId !== this.props.userId) {
this.fetchData();
}
}
componentWillUnmount() {
this.cancelRequest();
}
fetchData() {
// Fetch logic
}
}Function component with useEffect:
function DataFetcher({ userId }) {
useEffect(() => {
// componentDidMount + componentDidUpdate combined
const controller = new AbortController();
fetchData(userId, controller.signal);
// componentWillUnmount cleanup
return () => {
controller.abort();
};
}, [userId]); // Re-run when userId changes
const fetchData = async (id, signal) => {
// Fetch logic
};
}Key mappings:
- componentDidMount → useEffect(() => { }, [])
- componentDidUpdate → useEffect(() => { }, [dependencies])
- componentWillUnmount → return cleanup function from useEffect
If you cannot convert to a function component, wrap it with a HOC that uses hooks:
// Custom hook you want to use
function useWindowSize() {
const [size, setSize] = useState({ width: 0, height: 0 });
useEffect(() => {
const handleResize = () => {
setSize({ width: window.innerWidth, height: window.innerHeight });
};
handleResize();
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
// HOC that bridges hooks to class component
function withWindowSize(Component) {
return function WrappedComponent(props) {
const windowSize = useWindowSize(); // ✅ Hooks work here
return <Component {...props} windowSize={windowSize} />;
};
}
// Class component receives hook data as props
class MyClassComponent extends React.Component {
render() {
const { windowSize } = this.props;
return <div>Width: {windowSize.width}</div>;
}
}
// Export wrapped component
export default withWindowSize(MyClassComponent);This pattern allows you to use hooks without converting the class.
If your class component uses refs, convert them to useRef:
Class component with refs:
class InputFocus extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
focusInput = () => {
this.inputRef.current.focus();
};
render() {
return <input ref={this.inputRef} />;
}
}Function component with useRef:
function InputFocus() {
const inputRef = useRef(null); // ✅ useRef replaces createRef
const focusInput = () => {
inputRef.current.focus();
};
return <input ref={inputRef} />;
}Note: useRef is more flexible and can store any mutable value, not just DOM refs.
After converting from class to function component, verify:
1. State behavior: Ensure state updates work correctly
2. Effects timing: Check that useEffect runs at appropriate times
3. Cleanup: Verify cleanup functions execute on unmount
4. Props: Confirm component receives and uses props correctly
5. Context: If using context, test with useContext
6. Performance: Compare render counts with React DevTools Profiler
// Example test with React Testing Library
import { render, screen, fireEvent } from '@testing-library/react';
test('Counter increments', () => {
render(<Counter />);
const button = screen.getByText('Increment');
fireEvent.click(button);
expect(screen.getByText('Count: 1')).toBeInTheDocument();
});Use React DevTools to ensure the component doesn't have excessive re-renders after migration.
Why hooks don't work in classes: Hooks rely on React's internal call order and fiber architecture. Each hook call is stored in a linked list associated with the component's fiber node. When React renders a function component, it walks this list in order. Class components don't participate in this mechanism - they use instance properties and lifecycle methods instead, making hooks fundamentally incompatible.
Error boundaries remain class-only: As of React 18, error boundaries still require class components with componentDidCatch and getDerivedStateFromError. This is the one case where class components are still necessary. If you need an error boundary, keep it as a class or use a library like react-error-boundary.
Migration strategies for large codebases: Don't feel pressured to convert all class components at once. It's perfectly fine to have a mix of function and class components. Prioritize converting:
1. New components (always write as functions)
2. Leaf components (no children, easy to test)
3. Components that would benefit from custom hooks
4. Frequently modified components
Custom hooks for shared logic: One major benefit of converting to function components is the ability to extract and reuse stateful logic via custom hooks. This was harder with class components, which relied on HOCs or render props patterns:
// Custom hook replaces HOC pattern
function useAuth() {
const [user, setUser] = useState(null);
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(setUser);
return unsubscribe;
}, []);
return user;
}
// Simple usage in any function component
function Profile() {
const user = useAuth(); // ✅ Clean and reusable
return <div>{user?.name}</div>;
}ESLint enforcement: Use eslint-plugin-react-hooks to enforce Rules of Hooks automatically:
{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error"
}
}This catches invalid hook calls at lint time, before runtime errors occur.
Prop spreading could cause security issues
Prop spreading could cause security issues
Error: error:0308010C:digital envelope routines::unsupported
Error: error:0308010C:digital envelope routines::unsupported
React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render.
React Hook useEffect placed inside a condition
Hook can only be called inside the body of a function component
Hook can only be called inside the body of a function component
Rollup failed to resolve import during build
How to fix "Rollup failed to resolve import" in React