Lesson 9 of 80
The older way of writing components, built directly on JavaScript classes — and directly on the prototype chain underneath them.
13 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 4
Which capability still has no direct equivalent in function components with hooks?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Before hooks (2019), every stateful component was a class. A class component is still just a component — same props in, same JSX out — but this holds its state and lifecycle methods take the place of useEffect. Class components aren't deprecated; hooks just became the default because they're plainer functions with less ceremony.
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.handleClick = this.handleClick.bind(this); // see below
}
handleClick() {
this.setState((s) => ({ count: s.count + 1 }));
}
render() {
return <button onClick={this.handleClick}>{this.state.count}</button>;
}
}
Every JavaScript class sits on the prototype chain — Counter.prototype is where handleClick and render actually live, and every Counter instance delegates to it. This is the exact same mechanism from the JavaScript track's class and prototype-chain lessons, just wearing React's clothing.
.bind(this) is needed at all<button onClick={this.handleClick}>...</button>
This passes this.handleClick as a value — just the function, detached from the instance. When React later calls it, it calls it as a plain function, not as instance.handleClick(). From the this-binding lesson: the call site decides this for a normal function, not where it was defined — and the call site here is a bare function call, so this inside handleClick is undefined, not the component instance. .bind(this) in the constructor (or writing it as an arrow-function class field) permanently locks this to the instance, sidestepping the call site entirely.
class Counter extends React.Component — state lives on this.state, not a hook.
componentDidMount + componentDidUpdate + componentWillUnmount roughly cover what one useEffect does — but a class component has to manually compare prevProps/prevState inside componentDidUpdate to know what changed; a hook's dependency array does that comparison for you.shouldComponentUpdate is the class equivalent of React.memo — both are about skipping a re-render, not about the update/effect lifecycle.componentDidCatch / static getDerivedStateFromError) still require a class component — there is no hook for catching render errors in a subtree.this.setState is asynchronous and batched, same as a hook's setter — calling it doesn't update this.state immediately within the same function.Plenty of production code is still class components, and "why does this break in my event handler?" is a real, common bug — tracing it back to the call-site rule from the this-binding lesson (rather than memorizing ".bind() fixes it") is what shows you understand the mechanism, not just the workaround.