Component

Defines a React component as a JavaScript class with state, lifecycle methods and instance fields.

Signature
class YourComponent extends Component {
  constructor(props)
  state: object
  props: object
  context: any
  static contextType?: Context
  static defaultProps?: object
  static getDerivedStateFromProps?(props, state): object | null
  static getDerivedStateFromError?(error): object
  render(): ReactNode
  setState(nextState, callback?): void
  forceUpdate(callback?): void
  componentDidMount?(): void
  componentDidUpdate?(prevProps, prevState, snapshot?): void
  componentWillUnmount?(): void
  shouldComponentUpdate?(nextProps, nextState, nextContext): boolean
  getSnapshotBeforeUpdate?(prevProps, prevState): any
  componentDidCatch?(error, info): void
}

Parameters

ParameterTypeDescription
$propsobjectPassed to the constructor and forwarded to super(props). Available afterwards as this.props.
$nextStateobject | ((prevState, props) => object)Argument to setState: an object shallow-merged into state, or an updater function receiving the pending state.
$callbackoptional() => voidOptional second argument to setState and forceUpdate, run after the update has been applied to the DOM.
$prevPropsobjectFirst argument to componentDidUpdate and getSnapshotBeforeUpdate: the props before this update.
$prevStateobjectSecond argument to componentDidUpdate and getSnapshotBeforeUpdate: the state before this update.
$snapshotoptionalany= undefinedThird argument to componentDidUpdate, the value returned by getSnapshotBeforeUpdate, or undefined if that method is not defined.

Errors

Referencing `this` before calling super(props) in the constructor throws a ReferenceError. Returning undefined from render does not throw — since React 18, render may return JSX, a string, a number, a portal, an array of nodes, null, undefined, true, or false.

Changelog

  • PHP 16.3.0componentWillMount, componentWillReceiveProps and componentWillUpdate were deprecated in favour of UNSAFE_-prefixed aliases; getDerivedStateFromProps and getSnapshotBeforeUpdate were added.
  • PHP 18.0State updates are batched everywhere, including inside promises and timeouts, so two setState calls in an async callback now produce one render instead of two.
  • PHP 19.0`propTypes` checks were removed from the React package for all components, classes included, and are now silently ignored. `defaultProps` was removed only for function components; class components still support it, since there is no ES6 alternative.

The class form of a component. React does not recommend it for new code — hooks cover the same ground with less ceremony — but it is what most pre-2019 codebases are written in, and it remains the only way to write an error boundary, since componentDidCatch and getDerivedStateFromError have no hook equivalent.

How the pieces map to hooks

  • this.state plus setState → useState or useReducer
  • componentDidMount + componentDidUpdate + componentWillUnmount → one useEffect with a cleanup function
  • shouldComponentUpdate → memo, or PureComponent

What trips people up

setState is asynchronous and shallow-merges. Reading this.state on the line after calling it gives the old value, and two calls to this.setState({n: this.state.n + 1}) in a row increment once — pass an updater function instead. Methods are not bound: an event handler passed as onClick={this.handleClick} sees this as undefined unless you bind it in the constructor or declare it as a class field arrow function. In Strict Mode the constructor and render run twice in development, so any side effect placed there happens twice.

Example

Try it yourself
Loading editor…
Loading React…

When to use it

  • Maintain and extend a legacy class-based codebase
  • Write an error boundary, which still requires a class
  • Set up and tear down a subscription with componentDidMount and componentWillUnmount

Frequently asked questions

Why is this.state not updated right after setState?

setState queues an update rather than assigning immediately, and React batches queued updates before re-rendering. Read the new value in the setState callback or in componentDidUpdate.

Are class components deprecated in React?

No. They still work and are not scheduled for removal, but React recommends function components for new code and only classes can implement error boundaries.

Why is this undefined inside my class method?

Class methods are not bound to the instance, so passing one as a callback loses this. Bind it in the constructor or declare the method as a class field arrow function.

More examples

An error boundary — the one thing hooks cannot do

Instead of unmounting the whole tree, the boundary renders 'Something went wrong: render failed'. Note that this only catches errors thrown during rendering and in lifecycle methods — not in event handlers or async callbacks.

Example · jsx
import { Component } from 'react';

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { error: null };
  }

  // Pure: decides what to render instead of the crashed subtree
  static getDerivedStateFromError(error) {
    return { error };
  }

  // Side effects: logging goes here, not in the method above
  componentDidCatch(error, info) {
    console.error(error, info.componentStack);
  }

  render() {
    if (this.state.error) {
      return <p>Something went wrong: {this.state.error.message}</p>;
    }
    return this.props.children;
  }
}

function Broken() {
  throw new Error('render failed');
}

export default function App() {
  return (
    <ErrorBoundary>
      <Broken />
    </ErrorBoundary>
  );
}

The mount / update / unmount trio

Shows the three-method pattern that a single useEffect with a dependency array and a cleanup function replaces. The duplication between componentDidMount and componentDidUpdate is exactly the bug source hooks were designed to remove.

Example · jsx
import { Component } from 'react';

class ChatRoom extends Component {
  componentDidMount() {
    this.connect();
  }

  componentDidUpdate(prevProps) {
    // Props changed: tear the old connection down first
    if (prevProps.roomId !== this.props.roomId) {
      this.disconnect();
      this.connect();
    }
  }

  componentWillUnmount() {
    this.disconnect();
  }

  connect() {
    this.connection = { roomId: this.props.roomId, open: true };
  }

  disconnect() {
    if (this.connection) {
      this.connection.open = false;
      this.connection = null;
    }
  }

  render() {
    return <p>Connected to {this.props.roomId}</p>;
  }
}

export default function App() {
  return <ChatRoom roomId="general" />;
}

The same counter as a function component

The same behaviour in a third of the code: no constructor, no super(props), no binding, and no this. The updater form of the setter plays the same role as the updater form of setState.

Example · jsx
import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount((c) => c + 1);
  }

  return (
    <div>
      <h3>Count: {count}</h3>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

Discussion

  • Be the first to comment on this lesson.