React Class Components

Before Hooks existed, React components were written as ES6 classes with their own internal state and lifecycle methods, and you'll still meet this style in older codebases today.

Why Class Components Still Matter

React shipped with class-based components for its first several years. Function components with Hooks are now the default way to write new code, but a huge amount of production React — especially in enterprise apps, older libraries, and error boundary implementations — still uses classes. Understanding the class syntax lets you read, maintain, and gradually migrate that code.

Defining a Class Component

A class component is a JavaScript class that extends React.Component and implements a render() method returning JSX. The class receives props automatically through its constructor and through this.props anywhere in the class body.

A Basic Class Component

import React from 'react';

class Greeting extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

export default Greeting;

State with this.state and this.setState

Class components keep local state as a single object assigned to this.state, usually initialized in the constructor. You never mutate this.state directly; instead you call this.setState(), which merges the given fields into the existing state object and schedules a re-render.

A Counter Using State

import React from 'react';

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState((prevState) => ({ count: prevState.count + 1 }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Clicked {this.state.count} times
      </button>
    );
  }
}

export default Counter;
Note: Always call super(props) before touching this in the constructor. Skipping it, or forgetting to pass props, leaves this.props undefined until React finishes constructing the instance.

Binding Event Handlers

Inside a class, regular methods do not automatically bind 'this' to the component instance. If you pass this.handleClick directly to onClick without binding it, calling this.setState inside the handler throws because 'this' is undefined. The classic fixes are binding in the constructor (as above), using an arrow function class property, or wrapping the call in an inline arrow function in render.

  • Bind in the constructor: this.handleClick = this.handleClick.bind(this)
  • Use a class field arrow function: handleClick = () => { ... }
  • Wrap at call site: onClick={() => this.handleClick()}
  • Avoid re-binding inside render for performance-sensitive lists

Lifecycle Basics

Class components expose lifecycle methods that fire at specific points in a component's existence: mounting, updating, and unmounting. The three you'll use most often are componentDidMount, componentDidUpdate, and componentWillUnmount.

MethodRuns WhenCommon Use
componentDidMountRight after the first renderFetch data, set up subscriptions
componentDidUpdateAfter every re-render except the firstReact to prop/state changes
componentWillUnmountJust before the component is removedClean up timers, listeners, sockets

Fetching Data on Mount

import React from 'react';

class UserProfile extends React.Component {
  constructor(props) {
    super(props);
    this.state = { user: null };
  }

  componentDidMount() {
    fetch(`/api/users/${this.props.userId}`)
      .then((res) => res.json())
      .then((user) => this.setState({ user }))
      .catch(() => {
        // No backend available in this demo, so fall back to sample data
        // instead of leaving the component stuck on "Loading...".
        this.setState({ user: { name: 'Demo User' } });
      });
  }

  render() {
    if (!this.state.user) return <p>Loading...</p>;
    return <h2>{this.state.user.name}</h2>;
  }
}

UserProfile.defaultProps = {
  userId: 1,
};

export default UserProfile;
Note: The Hooks equivalent of componentDidMount + componentDidUpdate + componentWillUnmount combined is a single useEffect call. Keep that mapping in mind when reading migration guides.

Exercise: React Class Components

What method must every class component define in order to display UI?