Functional vs class components in React: what actually differs?

LowIntermediateReact
Preparing for interviews?

Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.

Quick Answer

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.

Answer

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 React.Component with a render() method

State

useState / useReducer

this.state + this.setState()

Side effects / lifecycle

useEffect / useLayoutEffect (+ cleanup)

Lifecycle methods (componentDidMount/Update/WillUnmount)

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 + this, lifecycle phases

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

High-level differences that matter in real code

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

useEffect(() => { ... }, [])

componentDidMount()

Run on specific changes

useEffect(() => { ... }, [a, b])

componentDidUpdate(prevProps, prevState) + comparisons

Cleanup on unmount

return () => cleanup() inside effect

componentWillUnmount()

Sync DOM measurements before paint

useLayoutEffect

componentDidMount/Update (but timing differs; layout effect is the closer analogue)

How to translate between the two styles
JSX
// 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 selection guidance

Practical scenario
You migrate a class-based form to hooks to share validation logic across components.

Common pitfalls

      • Trying to use this or lifecycle methods directly in a function component.
      • Missing useEffect dependencies, causing stale state.
      • Overusing hooks in complex components without refactoring.
Trade-off or test tip
Hooks are flexible but rely on rules. Test lifecycle behavior and add lint rules for hooks.

Summary

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.

Similar questions
Guides
25 / 41