React.Component — class components and how they work

Author: IT Sectr Published: 2026-07-04 Reading time: 9 min

React.Component is the base class for creating components using inheritance in React. Before the introduction of hooks in version 16.8, class components were the only way to manage state and lifecycle. According to React, 2024, class components are still supported, although functional components with hooks are recommended for new projects.

Key Takeaways

  • React.Component is the base class from which class components inherit via extends Component.
  • State is managed through this.state and updated only via this.setState — direct mutation is prohibited.
  • Lifecycle includes componentDidMount, componentDidUpdate, and componentWillUnmount methods.
  • this in class methods requires context binding — via bind, arrow functions, or class fields.
  • Migration to functional components can be gradual: hooks and classes can coexist in the same project.

What is React.Component?

React.Component is an abstract class that provides built-in methods for working with state, props, and the component lifecycle. To create a class component, you define a class that extends React.Component and implement the render method.

React.Component appeared in the very first version of React (2013) and remained the primary way to create components for five years. The React ecosystem was built around classes: higher-order components (HOC), render props, context — all these patterns were implemented using class syntax.

With the release of React 16.8 in 2019, the React team introduced hooks as an alternative to classes. Hooks solved the same problems — state, effects, context — but without inheritance and complex context binding. React.Component remains fully supported, but the functional approach is recommended for new projects.

Creating a Class Component

The minimal class component is a class with a single render method that returns a React element. render is the only required method. It must be a pure function of this.props and this.state: the same set of props and state produces the same result.

js
import React, { Component } from 'react';

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

Constructor and State Initialization

The constructor executes before mounting. It calls super(props) to pass props to the parent class and initializes this.state. Using the constructor for other purposes — subscriptions, API calls — is not recommended; componentDidMount is the right place for that.

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

  render() {
    return <p>Count: {this.state.count}</p>;
  }
}

State Management with this.state and this.setState

this.state is an object containing the component's data. Mutating it directly (this.state.count = 1) is forbidden — it does not trigger a re-render and breaks predictability. State must be updated through this.setState, which creates a new state object and triggers a re-render.

setState can accept an object or a function. The functional form is preferred when the new state depends on the previous one: this.setState(prev => ({ count: prev.count + 1 })). React may batch multiple setState calls together for performance optimization.

js
class Toggle extends Component {
  constructor(props) {
    super(props);
    this.state = { isOn: false };
  }

  handleClick() {
    this.setState(prev => ({ isOn: !prev.isOn }));
  }

  render() {
    return (
      <button onClick={() => this.handleClick()}>
        {this.state.isOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

An important property of setState is that it is asynchronous. After calling setState, the state does not update immediately. If you need to perform an action after the update, pass a callback as the second argument: this.setState(newState, () => console.log(this.state)).

Lifecycle Methods

Class components have predefined methods that React calls at specific moments. Mounting — constructor → render → componentDidMount. Updating — render → componentDidUpdate. Unmounting — componentWillUnmount.

MethodPhasePurpose
constructorMountingInitialize state and bind methods
renderEvery timeReturn React elements (required)
componentDidMountAfter mountingAPI requests, subscriptions, timers
componentDidUpdateAfter updateReact to prop/state changes
componentWillUnmountBefore removalClean up subscriptions, timers, event listeners

ComponentDidMount — Fetching Data

componentDidMount is the primary place for side effects after the first render. Here you perform HTTP requests, establish WebSocket connections, and start timers. If you do not clean up the subscription in componentWillUnmount, a memory leak occurs.

js
class UserProfile extends Component {
  componentDidMount() {
    fetch(`/api/users/${this.props.userId}`)
      .then(res => res.json())
      .then(data => this.setState({ user: data }));
  }

  componentWillUnmount() {
    // Cancel request on unmount
  }

  render() {
    return <div>{this.state.user?.name}</div>;
  }
}

Binding the this Context in Methods

In JavaScript, class methods are not bound to the instance by default. If you pass a method as a callback — to an event handler, to a timer — this will be undefined (in strict mode) or window (without strict mode). Without binding, this.setState will throw an error.

There are three ways to bind: bind in the constructor, an arrow function in the callback, and a class field with an arrow function. The first method is the official React recommendation: bind once in the constructor, without recreating the function on every render.

js
// Method 1: bind in constructor (recommended)
class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState({ clicked: true });
  }

  render() {
    return <button onClick={this.handleClick}>Click</button>;
  }
}

Class fields — modern syntax with automatic binding. An arrow function in a class property captures this from the constructor's scope. The downside is that the method is created on each instance, not on the prototype, which increases memory usage for hundreds of components of the same type.

Class vs Functional Components

The choice between class and function is not just a matter of syntax. Functional components with hooks solve problems inherent to classes: duplication of logic across different lifecycle methods (componentDidMount + componentDidUpdate), complexity with this, and the inability to extract related logic into a reusable block.

Functional components are easier to test — they have no internal state (which before hooks was only available in classes) and do not require DOM mounting to verify callbacks. React recommends functional components for all new projects; documentation and examples are written using them.

Class components remain the best choice when working with legacy code. If a project was started before 2019, it likely contains thousands of class components. Rewriting all of them to hooks in a single sprint is risky. React guarantees backward compatibility: class components will not be removed.

Migration Strategy from Classes to Hooks

Gradual migration begins with analyzing the component. Simple stateless components are translated first — they are trivially replaced with functions. Next — components with one or two state fields, where useState directly replaces this.state and this.setState.

Complex components with multiple lifecycle methods are translated using useEffect. Logic from componentDidMount + componentDidUpdate is split into separate useEffect calls with different dependencies. componentWillUnmount is replaced by a return function inside useEffect. For complex state logic, use useReducer.

Automated migration tools — @phenomnomnominal/ts-react-class-to-hook or codemod scripts — can automatically convert simple classes. For complex cases, migration remains manual work requiring an understanding of the source component's logic.

Frequently Asked Questions

Is calling super(props) required in the constructor?

Yes. super(props) passes props to the parent React.Component. Without this call, this.props will be undefined inside the constructor. In render and lifecycle methods, props are available even without super, but this is incidental implementation behavior.

Why is setState asynchronous?

React batches multiple setState calls into a single batch to reduce unnecessary re-renders. If setState were synchronous, each call would trigger an immediate render, reducing performance during mass updates.

Can I use hooks inside a class component?

No. Hooks only work in functional components. For projects with class components, all the old patterns are available: render props, HOC, and Consumer for context. Migrating to hooks requires converting the class into a function.

What is PureComponent and how is it different from Component?

React.PureComponent implements shouldComponentUpdate with a shallow comparison of props and state. Unlike Component, which always re-renders on setState, PureComponent skips re-rendering if the data has not changed. This is an optimization for components without complex nesting.

How do I handle an error in a class component?

Use componentDidCatch and getDerivedStateFromError. componentDidCatch(error, info) logs the error. getDerivedStateFromError updates state to display a fallback UI. These methods only work in class components — functions need a wrapping ErrorBoundary component.

Summary

  • React.Component is the base class for components with inheritance, state, and lifecycle methods.
  • this.state is initialized in the constructor, updated only via this.setState — direct mutation is prohibited.
  • componentDidMount is the primary place for API requests, subscriptions, and timers after the first render.
  • componentWillUnmount is required cleanup of subscriptions and timers to prevent memory leaks.
  • Binding this is required: bind in the constructor is the recommended approach for callback methods.
  • Hooks have replaced class components for new projects, but classes remain fully supported.
  • Migration to hooks is done gradually: stateless components first, complex ones via useReducer and useEffect.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also