React originally supported class components as the main way to manage state and lifecycle methods. However, since React 16.8, functional components with hooks have become the standard. Hooks allow developers to use state, lifecycle, and other React features inside simple functions, making class components largely unnecessary.
Limitations of Class Components
- Complexity: Managing state and lifecycle methods (componentDidMount, componentDidUpdate, etc.) often led to verbose and harder-to-read code.
- Reusability: Sharing logic between components required patterns like higher-order components or render props, which added complexity.
- Learning Curve: Beginners had to learn both functional and class syntax, making React harder to grasp.
- Boilerplate: More code was needed compared to functional components.
How Hooks Replaced Classes
- useState replaces this.state for managing local state.
- useEffect replaces lifecycle methods (componentDidMount, componentDidUpdate, componentWillUnmount).
- useContext replaces context usage in classes.
- useRef replaces instance variables for storing mutable values.
- Custom Hooks allow developers to extract and reuse logic across components without complex patterns.
Why Functional Components + Hooks Are Preferred
- Simpler Syntax: Components are just functions returning JSX.
- Cleaner Code: Hooks reduce boilerplate and make logic easier to follow.
- Better Reusability: Custom hooks make sharing logic straightforward.
- Consistency: One unified way to build components (functions), instead of two different paradigms.
- Modern Standard: The React team recommends hooks for new projects, and most libraries/frameworks are built around them.
💡 In short: Class components were replaced because they were more complex and harder to reuse. Hooks brought state and lifecycle features into functional components, making React simpler, more powerful, and easier to maintain.
Class Component (Old Way)
import React from "react";
class Counter extends React.Component {
constructor(props) {
super(props);
// state stored in this.state
this.state = { count: 0 };
}
// lifecycle method
componentDidMount() {
console.log("Component mounted!");
}
// event handler
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increase</button>
</div>
);
}
}
export default Counter;
import React, { useState, useEffect } from "react";
function Counter() {
// state stored using useState
const [count, setCount] = useState(0);
// side effect handled with useEffect
useEffect(() => {
console.log("Component mounted!");
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increase</button>
</div>
);
}
export default Counter;