ForgeFrontend — Prepare, Practice, Crack
Secure checkout
Lifetime access
Instant PDF download
Free updates forever

Prepare · Practice · Crack

Class vs Functional Components in React

Short answer

A class component extends React.Component, holds state in this.state and runs code through lifecycle methods. A functional component is a plain function that returns JSX and uses hooks for state and effects. Hooks made functions capable of everything classes could do, so all new React code uses functions.

Class componentFunctional component
Declared asclass X extends React.Componentfunction X() { … }
Returns JSX fromA render() methodThe function body
Propsthis.propsThe function's argument
Statethis.state + this.setStateuseState / useReducer
Side effectscomponentDidMount / DidUpdate / WillUnmountuseEffect
State updatesMerged into the existing objectReplaced entirely
Logic reuseHOCs and render propsCustom hooks
Needs `this`Yes, with binding to get rightNo
Status in 2026Supported, legacyThe default for all new code
The same capabilities, reached two different ways.

The same component, written both ways

Before the differences, the shape. A class component is an object with a render method that React calls, plus lifecycle methods React calls at particular moments. A functional component is just a function React calls; hooks give it a place to keep values between those calls. Everything else follows from that difference.

A counter that also sets the document titlejsx
1// Class
2class Counter extends React.Component {
3  state = { count: 0 };
4
5  componentDidMount()  { document.title = this.state.count; }
6  componentDidUpdate() { document.title = this.state.count; }
7
8  increment = () => this.setState(s => ({ count: s.count + 1 }));
9
10  render() {
11    return <button onClick={this.increment}>{this.state.count}</button>;
12  }
13}
14
15// Function
16function Counter() {
17  const [count, setCount] = useState(0);
18
19  useEffect(() => { document.title = count; }, [count]);
20
21  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
22}
23// → identical behaviour, and the mount/update duplication is gone
24//   because useEffect describes a synchronisation, not a moment

That duplication between componentDidMount and componentDidUpdate is not an accident of this example — it is a structural feature of the class model. Lifecycle methods are organised by when React calls them, so one feature gets split across three methods and three unrelated features get mixed into one. Hooks organise by concern instead, which is the actual reason the migration happened.

Mapping lifecycle methods to hooks

In an interview you will often be asked for the hook equivalent of a lifecycle method. The mapping is mostly clean, with two important caveats: useEffect runs after paint while lifecycle methods run before it, and there is deliberately no equivalent for some of the legacy methods.

ClassHook equivalentCaveat
componentDidMountuseEffect(fn, [])Runs after paint, not before
componentDidUpdateuseEffect(fn, [deps])Also runs on mount
componentWillUnmountThe function returned from useEffectAlso runs before each re-run
shouldComponentUpdateReact.memoShallow props only, no state check
getDerivedStateFromPropsCompute during render, or a keyUsually a sign of a design problem
getSnapshotBeforeUpdateuseLayoutEffect (approximately)No exact equivalent
componentDidCatchNoneStill requires a class
this.forceUpdate()A dummy useState counterRarely the right answer
Lifecycle to hook, with the traps.

The row people get wrong is componentDidMount. It fires synchronously before the browser paints; useEffect fires after. A migration that swaps one for the other can introduce a visible flicker in any component that measures and repositions on mount — in which case useLayoutEffect, not useEffect, is the faithful translation.

`this` was the real cost of classes

JavaScript decides what `this` refers to by how a function is called, not where it is defined. A method extracted from an object — which is exactly what passing this.handleClick to onClick does — loses its receiver. Every React class developer hit this, and the workaround was boilerplate in every constructor.

Three fixes for one language quirkjsx
1class Toggle extends React.Component {
2  state = { on: false };
3
4  handleClick() { this.setState({ on: !this.state.on }); }
5
6  render() {
7    return <button onClick={this.handleClick}>toggle</button>;
8    // → TypeError: Cannot read properties of undefined (reading 'setState')
9  }
10}
11
12// Fix 1 — bind in the constructor (the classic boilerplate)
13constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); }
14
15// Fix 2 — a class field arrow function, which captures `this` lexically
16handleClick = () => { this.setState({ on: !this.state.on }); };
17
18// Fix 3 — bind at the call site, which allocates a new function per render
19<button onClick={() => this.handleClick()}>toggle</button>

Functional components delete the whole category. There is no receiver to lose, because there is no object — state comes from a closure over the hook's value, and handlers are ordinary functions. Interviewers like this question because it connects React to core JavaScript, so it is worth being able to explain the binding rule itself, not just the fix.

This is 1 of 75+ questions in the React Interview Kit

Get every question with detailed answers, follow-ups and real code — plus coding challenges and a last-minute revision sheet. One-time payment, instant access.

⚡ Get the React Interview Kit → ₹399

The stale-props bug classes have and hooks do not

This is the subtle one, and it is worth knowing because it inverts the usual assumption that hooks are the confusing model. A class reads this.props at the moment code runs, and `this` always points at the latest render's props. So an async callback started in one render reads props from a later render — silently giving you data that belongs to a different request.

Same logic, different outcomejsx
1// Class: reads this.props.id AFTER the delay
2class Loader extends React.Component {
3  handleClick = () => {
4    setTimeout(() => console.log(this.props.id), 3000);
5  };
6}
7// Click with id=1, then navigate so id becomes 2:
8// → 2   — the wrong id, from a render that happened later
9
10// Function: id was captured by the closure at click time
11function Loader({ id }) {
12  const handleClick = () => {
13    setTimeout(() => console.log(id), 3000);
14  };
15}
16// Same sequence:
17// → 1   — the id that was on screen when the user clicked

Each render of a function component gets its own props, its own state and its own handlers, frozen for that render. People meet this as the stale closure problem when they want the newest value inside an interval — solved with a ref or a functional update — but it is the same property that makes the correct case correct by default. Being able to argue both directions is a genuinely senior answer.

Reusing stateful logic

This was the motivation the React team led with. Classes had no way to share stateful behaviour between components, so the ecosystem invented higher-order components and render props — patterns that work but wrap your tree in layers that exist purely for plumbing, and that collide when two of them want the same prop name.

Wrapper hell versus a custom hookjsx
1// Class era: three concerns, three wrappers, unreadable devtools
2export default withRouter(
3  connect(mapState)(
4    withTheme(UserPanel)
5  )
6);
7// → <Route><Connect><Theme><UserPanel/></Theme></Connect></Route>
8
9// Hooks: the same three concerns, no wrappers, one flat component
10function UserPanel() {
11  const params = useParams();
12  const user   = useSelector(selectUser);
13  const theme  = useTheme();
14  const online = useOnlineStatus();   // your own, reusable everywhere
15}
A custom hook is just a function that calls hooksjsx
1function useOnlineStatus() {
2  const [online, setOnline] = useState(navigator.onLine);
3
4  useEffect(() => {
5    const on = () => setOnline(true);
6    const off = () => setOnline(false);
7    window.addEventListener("online", on);
8    window.addEventListener("offline", off);
9    return () => {
10      window.removeEventListener("online", on);
11      window.removeEventListener("offline", off);
12    };
13  }, []);
14
15  return online;
16  // → each component calling this gets its OWN state,
17  //   sharing the logic and not the value
18}

What still requires a class in 2026

One thing, honestly: error boundaries. There is no hook for componentDidCatch or getDerivedStateFromError, so catching render errors in a subtree still means writing a class — or using react-error-boundary, which is a thin wrapper around exactly such a class.

The one class you may still writejsx
1class ErrorBoundary extends React.Component {
2  state = { error: null };
3
4  static getDerivedStateFromError(error) { return { error }; }
5
6  componentDidCatch(error, info) { logToService(error, info); }
7
8  render() {
9    if (this.state.error) return this.props.fallback;
10    return this.props.children;
11  }
12}
13// → catches errors thrown while rendering anything below it.
14//   Note what it does NOT catch: event handlers, async code,
15//   and errors thrown during server rendering.

Everything else has a hooks answer. getSnapshotBeforeUpdate is approximated by useLayoutEffect, shouldComponentUpdate by React.memo, and getDerivedStateFromProps by deriving during render or remounting with a key. The remaining reason to read class code is that it exists in the codebase you are joining.

Are class components deprecated?

No, and this is a distinction interviewers check. Class components are fully supported, they still work in React 19, and there is no announced removal. What has happened is that the documentation was rewritten around hooks, all new APIs ship hooks-only, and the ecosystem assumes function components. They are legacy in the sense that nobody starts new work with them, not in the sense of being scheduled for deletion.

SituationUseWhy
Any new componentFunction + hooksThe default everywhere
Error boundaryClassNo hook equivalent exists
Editing an existing classKeep the classMixed rewrites cause more bugs than they fix
A class you must add reuse toExtract a hook, wrap the classOr convert if the component is small
React Server ComponentsFunction onlyClasses are not supported there
Interview whiteboardFunction + hooksUnless the question is specifically about classes
Which model applies to what you are doing.

Can I use hooks inside a class component?

No. Hooks rely on React tracking calls in the order a function component makes them, and a class has no such call. To share hook-based logic with a class, wrap the class in a small function component that calls the hook and passes the result down as a prop.

Are functional components faster than class components?

Marginally, and not for the reason usually given. Function components produce slightly smaller bundles and avoid class instantiation, but the difference is negligible in practice. The real wins are structural: less code, no binding boilerplate, and logic that can be extracted into hooks.

What is the difference between setState and the useState setter?

this.setState shallowly merges the object you pass into existing state, so updating one key leaves the others intact. The useState setter replaces the value entirely, which is why you write setUser(prev => ({ ...prev, name })) when the state is an object.

Do function components have a constructor?

No, and they do not need one. Work that belonged in a constructor goes in useState's initial value, and for expensive setup you pass a function — useState(() => compute()) — so it runs only on the first render rather than on every one.

What were pure functional components before hooks?

Function components existed from the start but could only receive props and return JSX — they were called stateless functional components for that reason. Hooks, added in React 16.8, gave them state, effects, context and refs, which is what made classes unnecessary.

Frequently asked questions

What is the difference between class and functional components in React?
A class component extends React.Component, stores state in this.state, and runs code in lifecycle methods. A functional component is a plain function returning JSX that uses hooks for state, effects and context. Since hooks arrived in React 16.8 they have identical capabilities, with one exception: error boundaries.
Why did hooks replace class components?
Because lifecycle methods organise code by when it runs rather than by what it does, splitting one feature across three methods and merging unrelated features into one. Hooks group by concern, remove this-binding entirely, and make stateful logic extractable into custom hooks — which HOCs and render props could only approximate with wrappers.
Are class components deprecated in React?
No. They are fully supported, work in React 19, and have no announced removal date. They are legacy in the practical sense: the documentation is hooks-first, new APIs are hooks-only, and no team starts new components with them.
Can I use hooks in a class component?
No. Hooks depend on React tracking the order of calls within a function component's render, which a class never makes. To use hook logic from a class, wrap it in a small function component that calls the hook and passes the value down as a prop.
What is the useEffect equivalent of componentDidMount?
useEffect(fn, []) with an empty dependency array runs once after the first render. The one difference worth stating is timing: componentDidMount runs before the browser paints, useEffect after. When that matters — measuring and repositioning — useLayoutEffect is the accurate equivalent.
Do I still need to learn class components?
Enough to read them. You will meet classes in older codebases, in tutorials written before 2019, and in interview questions about lifecycle methods. Write functions for anything new, but be able to explain this-binding, setState merging, and the lifecycle-to-hook mapping.
Which is better, class or functional components?
Functional components, for all new work — less code, no binding, better logic reuse, and full support in Server Components. The one thing classes still do that functions cannot is act as an error boundary, so most codebases keep exactly one class for that.
What is the difference between this.setState and useState?
this.setState merges the object you pass into existing state, so other keys survive untouched. The useState setter replaces the value outright, which is why object state needs the spread pattern. Both batch updates and both offer an updater-function form for reading the previous value safely.
Why do class components need to bind this?
Because JavaScript resolves this from how a function is called. Passing this.handleClick to onClick detaches the method from its object, leaving this undefined in strict mode. The modern fix is a class field arrow function, which captures this lexically and needs no constructor binding.
Do functional components have lifecycle methods?
Not as named methods — useEffect covers the same ground. Mount is an empty dependency array, update is a populated one, and unmount is the cleanup function you return. That cleanup also runs before each re-run, which has no direct class equivalent.
Can functional components have state?
Yes, since React 16.8, through useState and useReducer. Before hooks they could not, which is why older material calls them stateless functional components. That name is now misleading and worth avoiding.
How do I convert a class component to a functional one?
Move this.state into useState calls, turn componentDidMount and componentDidUpdate into useEffect with the right dependencies, return the unmount logic from that effect, replace this.props with the function's argument, and drop the bindings. Check any mount-time measurement — it may need useLayoutEffect to avoid a new flicker.

This is 1 of 75+ questions in the React Interview Kit

Get every question with detailed answers, follow-ups and real code — plus coding challenges and a last-minute revision sheet. One-time payment, instant access.

⚡ Get the React Interview Kit → ₹399
Written by Arun Karthikeyan · Last updated

Full kit

React Interview Kit · ₹399

Get it →