Back to React
React
medium
mid

What is the difference between class components and functional components in React?

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.

4 min read·~6 min to think through

Both produce the same output; they differ in API and ergonomics. Function components + Hooks are the modern standard.

Class components (legacy)

jsx
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 via this.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)

jsx
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 useEffectgrouped by concern, not scattered across lifecycles.
  • No this.

Key differences

ClassFunction
SyntaxES6 classplain function
Statethis.state / setState (merges)useState (replaces)
Side effectslifecycle methodsuseEffect
thisyes — binding pitfallsnone
Logic reuseHOCs / render propscustom Hooks
Boilerplatemoreless

Why Hooks won

  • Logic reuse — custom Hooks extract stateful logic cleanly; the old HOC/render-prop patterns caused "wrapper hell."
  • Co-locationuseEffect groups related setup + cleanup together, instead of splitting one feature across componentDidMount and componentWillUnmount.
  • 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.

Senior engineer discussion

Seniors explain that capability is equivalent and focus on why Hooks won — logic reuse via custom Hooks, effect co-location, no `this` — while noting classes aren't deprecated and error boundaries remain class-only.

Related questions