react-class-to-hooks
react-class-to-hooks rule
Suggest migrating React class components to hooks with detailed migration path. This rule is part of eslint-plugin-react-features and provides LLM-optimized error messages with fix suggestions.
💡 Provides suggestions | 🔧 Automatically fixable
Quick Summary
| Aspect | Details |
|---|---|
| Severity | Warning (migration guidance) |
| Auto-Fix | ✅ Yes (suggests hooks migration) |
| Category | React / Migration |
| ESLint MCP | ✅ Optimized for ESLint MCP integration |
| Best For | React projects migrating from class to functional components |
Rule Details
Identifies React class components that can be migrated to functional components with hooks, providing complexity analysis and migration guidance.
A React class component is a class that extends React's Component or
PureComponent, however the file names it (Component, a renamed import,
React.Component, a namespace import), declared, written as a class
expression, or as an anonymous export default class. A class extending a
Component the file declares itself or imports from another library is not
one.
An error boundary (a class with componentDidCatch or a static
getDerivedStateFromError) is never reported: React has no hooks equivalent,
and documents that an error boundary cannot be written as a function
component.
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
ignorePureRenderComponents | boolean | false | Skip classes that extend PureComponent |
allowComplexLifecycle | boolean | false | Skip classes using getDerivedStateFromProps or getSnapshotBeforeUpdate (no simple hook mapping) |
Examples
❌ Class Component
class UserProfile extends React.Component<Props, State> {
state = {
loading: false,
data: null
};
componentDidMount() {
this.fetchData();
}
fetchData = async () => {
this.setState({ loading: true });
const data = await api.getUser(this.props.userId);
this.setState({ data, loading: false });
};
render() {
const { loading, data } = this.state;
return loading ? <Spinner /> : <div>{data?.name}</div>;
}
}✅ Functional Component with Hooks
function UserProfile({ userId }: Props) {
const [loading, setLoading] = useState(false);
const [data, setData] = useState<User | null>(null);
useEffect(() => {
async function fetchData() {
setLoading(true);
const userData = await api.getUser(userId);
setData(userData);
setLoading(false);
}
fetchData();
}, [userId]);
return loading ? <Spinner /> : <div>{data?.name}</div>;
}Configuration Examples
{
rules: {
'react-features/react-class-to-hooks': ['warn', {
ignorePureRenderComponents: false,
allowComplexLifecycle: false
}]
}
}Lifecycle Method Mapping
| Class Lifecycle | Hook Equivalent |
|---|---|
componentDidMount | useEffect(() => {}, []) |
componentDidUpdate | useEffect(() => {}, [deps]) |
componentWillUnmount | useEffect(() => () => {}, []) |
shouldComponentUpdate | React.memo() |
getDerivedStateFromProps | useState + useEffect |
Comparison with Alternatives
| Feature | react-class-to-hooks | eslint-plugin-react-hooks | codemods |
|---|---|---|---|
| Class Detection | ✅ Yes | ❌ No | ✅ Yes |
| Migration Suggestions | ✅ Yes | ❌ No | ⚠️ Automated only |
| Auto-Fix | ✅ Yes | ❌ No | ✅ Yes |
| LLM-Optimized | ✅ Yes | ❌ No | ❌ No |
| ESLint MCP | ✅ Optimized | ❌ No | ❌ No |
| Complexity Analysis | ✅ Yes | ❌ No | ❌ No |
Related Rules
react-no-inline-functions- Performance optimizationrequired-attributes- React attribute enforcementno-deprecated-api- API modernization
Further Reading
- React Hooks Documentation - React hooks reference
- Migrating from Classes to Hooks - Migration guide
- React Codemods - Automated migration tools
- ESLint MCP Setup - Enable AI assistant integration
Known False Negatives
createReactClass({...})components are not covered; only classes.- A superclass the file reaches through a helper (
class A extends withRouter(Component)) is not followed.
Use this rule
npm install -D oxlint-plugin-react-features{ "jsPlugins": ["oxlint-plugin-react-features"], "rules": { "react-features/react-class-to-hooks": "error" }}import { defineConfig } from 'vite-plus';export default defineConfig({ lint: { jsPlugins: ['oxlint-plugin-react-features'], rules: { 'react-features/react-class-to-hooks': 'error', }, },});oxlint-plugin-react-features re-exports eslint-plugin-react-features/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.
Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.