A class component is a JavaScript class that extends React.Component and returns JSX from a render() method. It reads props from this.props, keeps state in this.state, and updates it with this.setState(). Function components with hooks replaced them for new code, but classes are still fully supported, appear in many older codebases, and are the only way to write an error boundary.
Each counter keeps its own state. Add a third <Counter label="Plums" /> and it starts at 0 too.
The anatomy of a class component
import { Component } from 'react';
class Welcome extends Component {
static defaultProps = { name: 'stranger' };
constructor(props) {
super(props); // required before using this
this.state = { visits: 1 };
}
render() {
return <h1>Hello, {this.props.name}. Visit #{this.state.visits}</h1>;
}
}
- The class extends
Component(orReact.Componentwithimport React from 'react'). render()is required. It must be pure, like a function component's body: readthis.propsandthis.state, return JSX.this.propsholds the props. They are read only.- State can be set in a
constructor(callsuper(props)first) or, more commonly today, as a class field:state = { visits: 1 };. static defaultPropsstill works for classes in React 19 (it was removed only for function components).
this.setState merges, and can take an updater
setState does two things differently from the useState setter. It merges the object you pass into the current state, so you update only the keys you name. And, like every React state update, it is not applied immediately, so use the updater form when the next value depends on the previous one.
"+3 with objects" adds one, because all three calls read the same this.state.likes. "+3 with updaters" adds three. "Move to Paris" keeps name and likes (the merge) but loses zip, because the merge is one level deep. Write address: { ...this.state.address, city: 'Paris' } to keep it. The optional second argument to setState runs after the update, and the console shows the new address there.
Binding event handlers
A method passed as onClick={this.handleClick} is called later as a plain function, without its object, so this inside it is undefined.
The first button logs a TypeError about reading setState of undefined. The second works, because an arrow function in a class field captures this when the instance is created. The older fix, still common in legacy code, binds in the constructor:
constructor(props) {
super(props);
this.state = { on: false };
this.handleClick = this.handleClick.bind(this);
}
An inline arrow, onClick={() => this.handleClick()}, also works.
Lifecycle methods
Classes react to mounting, updating and unmounting through methods with fixed names. The ones you will see most:
componentDidMount(): after the first render is in the DOM. Start timers, subscriptions, requests.componentDidUpdate(prevProps, prevState): after every update. Compare with the previous values before doing work, or you will loop.componentWillUnmount(): before removal. Undo whatcomponentDidMountstarted.shouldComponentUpdate(nextProps, nextState): returnfalseto skip a render.
The component lifecycle page has the full list, the order they run in, and a component that logs every one.
Converting a class to a function component
Here is a class that uses all three common lifecycle methods. It ticks every second, logs when its label prop changes, and stops its timer when removed.
And the same component as a function. Each lifecycle method becomes part of an effect, and related setup and cleanup sit next to each other instead of in two methods.
Both timers count the same way and log on every label change. The function version also logs once on mount, since an effect runs after the first render too. The steps for any conversion:
this.props.xbecomes a destructured prop,{ x }.- Each key of
this.statebecomes its ownuseState. Remember the setter replaces instead of merging. - Methods become plain functions inside the component. No binding needed.
componentDidMount,componentDidUpdateandcomponentWillUnmountbecomeuseEffectcalls, grouped by what they do rather than by when they run.createRefbecomesuseRef, andstatic contextTypewiththis.contextbecomesuseContextoruse.
Leave error boundaries as classes; they have no function equivalent.
PureComponent
A normal class component renders every time its parent does. PureComponent first compares the new props and state with the previous ones, one level deep, and skips the render when nothing changed. It is the class counterpart of React.memo.
Click the button: only PlainLabel rendered is logged, because PureLabel sees the same text. Pass text={'hello ' + clicks} to both and they both render on every click.
The comparison is shallow, so a prop that is a new object or function on every parent render (style={{ color: 'red' }}, onClick={() => ...}) defeats it. That is the same trap as with React.memo.
Things React 19 removed from classes
If you maintain old class code, a few patterns no longer work in React 19:
- String refs (
ref="input"withthis.refs.input). UsecreateRef()or a ref callback. - Legacy context (
contextTypes,childContextTypes,getChildContext). UsecreateContextwithstatic contextType. propTypesare ignored. Use TypeScript or your own checks.findDOMNodeis gone. Put a ref on the DOM element instead.
The old componentWillMount, componentWillReceiveProps and componentWillUpdate methods are legacy too. They still run, under both the old names and the UNSAFE_ names (UNSAFE_componentWillMount and so on), but they are deprecated and StrictMode warns about them; move their code to componentDidMount, componentDidUpdate or getDerivedStateFromProps.
Frequently Asked Questions
Are class components deprecated in React?
No. They are still supported in React 19 and will keep working, but the React docs recommend function components with hooks for new code. You still need a class for error boundaries.
What is the difference between class and function components?
A class component keeps state in this.state, updates it with this.setState, and reacts to the lifecycle through named methods. A function component uses hooks (useState, useEffect) for the same jobs, needs no this, and is shorter.
Does setState update the state immediately?
No. this.setState schedules an update and React renders later, so this.state still has the old value right after the call. Pass a function, this.setState((state) => ...), when the new value depends on the old one, or read the result in the second argument callback or componentDidUpdate.
Why is this undefined in my event handler?
A class method passed as onClick={this.handleClick} is called without its object, so this is undefined. Define the handler as a class field arrow function (handleClick = () => { ... }) or bind it in the constructor.
What is PureComponent?
A Component that skips rendering when its props and state are shallowly equal to the previous ones. It is the class version of React.memo.