Explain the difference between class components and functional components.
Class components use ES6 classes, this, lifecycle methods, and this.state/setState. Function components are plain functions using Hooks for state and effects. Functions + Hooks are the modern standard — less boilerplate, better logic reuse, no `this` confusion. Both can do the same things.
Both produce the same output; they differ in API and ergonomics. Function components + Hooks are the modern standard.
Class components (legacy)
class Counter extends React.Component {
state = { count: 0 };
componentDidMount() { /* setup */ }
componentDidUpdate(prevProps) { /* react to changes */ }
componentWillUnmount() { /* cleanup */ }
render() {
return <button onClick={() => this.setState({ count: this.state.count + 1 })}>
{this.state.count}
</button>;
}
}- State lives in
this.state, updated viathis.setState(merges). - Side effects split across lifecycle methods.
- Bound to
this— a frequent source of bugs (must bind handlers or use class fields).
Function components (modern)
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
/* setup */
return () => { /* cleanup */ };
}, []);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}- State via
useState/useReducer. - Side effects via
useEffect— grouped by concern, not scattered across lifecycles. - No
this.
Key differences
| Class | Function | |
|---|---|---|
| Syntax | ES6 class | plain function |
| State | this.state / setState (merges) | useState (replaces) |
| Side effects | lifecycle methods | useEffect |
this | yes — binding pitfalls | none |
| Logic reuse | HOCs / render props | custom Hooks |
| Boilerplate | more | less |
Why Hooks won
- Logic reuse — custom Hooks extract stateful logic cleanly; the old HOC/render-prop patterns caused "wrapper hell."
- Co-location —
useEffectgroups related setup + cleanup together, instead of splitting one feature acrosscomponentDidMountandcomponentWillUnmount. - No
this— eliminates a whole class of binding bugs. - Less boilerplate, easier to read and test.
Classes aren't deprecated and still work, but new code uses functions. The one thing only classes had — error boundaries — still requires a class today (or a library wrapper).
The framing
"Same capabilities, different API. Classes use this, this.state/setState, and lifecycle methods; function components use Hooks — useState, useEffect. Functions won because Hooks make logic reusable via custom Hooks instead of HOC wrapper-hell, co-locate effect setup and cleanup instead of scattering them across lifecycles, and drop this entirely. Classes still work and are still required for error boundaries, but new code is function components."
Follow-up questions
- •Why did the React team introduce Hooks?
- •What's the difference between setState merging and useState replacing?
- •How do custom Hooks improve on HOCs and render props?
- •What can class components still do that function components can't?
Common mistakes
- •Thinking function components can't have state or lifecycle — Hooks cover both.
- •Saying classes are deprecated — they're not, just not recommended for new code.
- •Forgetting that useState replaces while setState merges.
- •Not knowing error boundaries still require a class.
Performance considerations
- •Neither is inherently faster. Function components avoid class instantiation overhead and pair naturally with React.memo; classes use shouldComponentUpdate/PureComponent for the same goal.
Edge cases
- •Error boundaries — still class-only.
- •getSnapshotBeforeUpdate has no exact Hook equivalent.
- •Mixing class and function components in one tree is fine.
Real-world examples
- •Legacy codebases gradually migrating class components to Hooks.
- •Error boundary components still written as classes in otherwise all-function apps.