Why? Aligns with the current React runtime and future features (concurrency, RSC).
React’s modern features—Concurrent Rendering, Suspense, React Server Components—are designed around function components + Hooks. Class components still work, but they lock you out of new APIs and add boilerplate (binding, lifecycles, this
issues). Teams often keep reaching for classes out of habit or to copy old tutorials, only to discover that mixing paradigms complicates state management and hurts upgrade paths.
// Counter.tsx — idiomatic Hook‑based component
import { useState, useEffect } from "react";
export default function Counter({ step = 1 }: { step?: number }) {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`; // side‑effect in a Hook
}, [count]);
return (
<button
onClick={() => setCount((c) => c + step)}
className="rounded-xl bg-blue-600 px-4 py-2 font-medium text-white"
>
Clicked {count} times
</button>
);
}
What’s happening?
useState
) and side‑effects (useEffect
) live in a plain function—no this
, no bindings.// CounterClass.tsx — legacy approach
import { Component } from "react";
interface Props {
step?: number;
}
interface State {
count: number;
}
export default class CounterClass extends Component<Props, State> {
state: State = { count: 0 };
componentDidMount() {
document.title = `Count: ${this.state.count}`;
}
componentDidUpdate() {
document.title = `Count: ${this.state.count}`;
}
handleClick = () => {
this.setState((prev) => ({ count: prev.count + (this.props.step ?? 1) }));
};
render() {
const { count } = this.state;
return (
<button
onClick={this.handleClick}
className="rounded-xl bg-blue-600 px-4 py-2 font-medium text-white"
>
Clicked {count} times
</button>
);
}
}
// Extra boilerplate, harder to compose with modern features, and incompatible
// with React Server Components or certain concurrent patterns.
this
, lifecycle method maze, or manual bindings—just plain JavaScript functions.