Component
Defines a React component as a JavaScript class with state, lifecycle methods and instance fields.
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
| Parameter | Type | Description |
|---|---|---|
$props | object | Passed to the constructor and forwarded to super(props). Available afterwards as this.props. |
$nextState | object | ((prevState, props) => object) | Argument to setState: an object shallow-merged into state, or an updater function receiving the pending state. |
$callbackoptional | () => void | Optional second argument to setState and forceUpdate, run after the update has been applied to the DOM. |
$prevProps | object | First argument to componentDidUpdate and getSnapshotBeforeUpdate: the props before this update. |
$prevState | object | Second argument to componentDidUpdate and getSnapshotBeforeUpdate: the state before this update. |
$snapshotoptional | any= undefined | Third 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.stateplussetState→useStateoruseReducercomponentDidMount+componentDidUpdate+componentWillUnmount→ oneuseEffectwith a cleanup functionshouldComponentUpdate→memo, orPureComponent
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
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.
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.
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.
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