Functional components are plain functions with hooks for state and lifecycle. Class components use this.state and lifecycle methods. Modern React favors function components with hooks. Trade-offs matter: hooks simplify composition, but legacy code still uses classes, so test lifecycle and performance behavior.
Functional vs class components in React: what actually differs?
Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.
Core idea
Both function and class components can render the same UI. The difference is how they express state, side effects, and reuse.
Function components use Hooks (useState, useEffect, etc.). Class components use instance state (this.state) and lifecycle methods (componentDidMount, componentDidUpdate, componentWillUnmount).
Aspect | Functional (Hooks) | Class |
|---|---|---|
Definition | A function that returns a ReactNode (JSX, string/number, fragment, null, etc.) | An ES6 class extending |
State |
|
|
Side effects / lifecycle |
| Lifecycle methods ( |
Code reuse | Custom Hooks (share stateful logic without wrappers) | HOCs / render props / mixins (older patterns) |
Mental model | Render is a pure calculation; hooks add state/effects tied to render order | Instance methods + |
Interop | Can’t use class lifecycles; but can wrap classes / call into legacy libs via effects/refs | Can’t use Hooks inside classes |
Modern React direction | Primary model; new patterns and ecosystem examples are hook-first | Supported but mostly for legacy codebases |
Lifecycle mapping (common interview ask)
Hooks are not “magic faster”. They’re a different API that can express the same lifecycle behaviors with explicit dependencies + cleanup.
Goal | Hooks | Class lifecycle |
|---|---|---|
Run once on mount |
|
|
Run on specific changes |
|
|
Cleanup on unmount |
|
|
Sync DOM measurements before paint |
|
|
// Functional component (Hooks)
import React from 'react';
export function Counter() {
const [count, setCount] = React.useState(0);
// effect: run after commit; cleanup on unmount
React.useEffect(() => {
document.title = `Count: ${count}`;
const id = setInterval(() => {
// functional update avoids stale reads
setCount((c) => c + 1);
}, 1000);
return () => clearInterval(id);
}, []);
return (
<button onClick={() => setCount((c) => c + 1)}>
Count: {count}
</button>
);
}
// Class component equivalent
export class CounterClass extends React.Component {
state = { count: 0 };
_id = null;
componentDidMount() {
document.title = `Count: ${this.state.count}`;
this._id = setInterval(() => {
this.setState((s) => ({ count: s.count + 1 }));
}, 1000);
}
componentDidUpdate() {
document.title = `Count: ${this.state.count}`;
}
componentWillUnmount() {
clearInterval(this._id);
}
render() {
return (
<button onClick={() => this.setState((s) => ({ count: s.count + 1 }))}>
Count: {this.state.count}
</button>
);
}
}
Important nuance (don’t claim “functional is faster”)
There’s no inherent “function components are faster” rule. Performance depends on update patterns, memoization boundaries, and avoiding unnecessary work. Functions are preferred because Hooks give a simpler composition model for stateful logic and align with modern React features and patterns.
When to pick | Functional (recommended) | Class (still valid) |
|---|---|---|
New code | Default choice | Avoid unless you have a specific reason |
Legacy codebase | Incremental migration is common (mixing is fine) | Keep existing classes; rewrite opportunistically |
Reusable stateful logic | Custom hooks are the standard approach | Older patterns (HOC/render props) still exist but are heavier |
Very old React (< 16.8) | Hooks unavailable | Classes required |
Practical scenario
You migrate a class-based form to hooks to share validation logic across components.
Common pitfalls
- Trying to use
thisor lifecycle methods directly in a function component. - Missing
useEffectdependencies, causing stale state. - Overusing hooks in complex components without refactoring.
Hooks are flexible but rely on rules. Test lifecycle behavior and add lint rules for hooks.
Function components are the modern default: state and lifecycle behavior are expressed with Hooks (especially useState/useEffect) and logic reuse is done via custom hooks. Class components express the same ideas via this, this.state/setState, and lifecycle methods. React still supports classes, but new React code is typically hook-first.