PureComponent
Skips re-rendering a class component when its props and state are shallowly unchanged.
class YourComponent extends PureComponent {
render(): ReactNode
}- Added in React 15.3.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$props | object | Passed to the constructor and forwarded to super(props); shallowly compared against the next props on every update. |
Errors
Defining your own shouldComponentUpdate on a subclass warns in development, because it overrides the shallow comparison this class exists to provide.
Changelog
- PHP 15.3.0Introduced as the class equivalent of a shallow-comparing shouldComponentUpdate.
- PHP 19.0`propTypes` checks were removed from the React package for all components, including this one, and are now silently ignored. `defaultProps` was removed only for function components; class components, including this one, still support it.
Identical to Component in every respect except one: before re-rendering, React shallowly compares the incoming props and state against the current ones and skips render entirely if every key matches by reference. It is exactly a built-in shouldComponentUpdate doing Object.is on each key.
When to reach for it
When a class component is expensive to render, sits inside a parent that re-renders frequently, and receives props that genuinely stay the same across most of those renders — long list rows are the usual case. Measure first; the comparison itself has a cost, and for a cheap component it is a net loss.
What trips people up
Shallow means shallow. An inline object, array or arrow function prop is a new reference on every parent render, so style={{color: 'red'}} or onClick={() => save(id)} defeats the optimisation completely. The same applies to mutating state in place: this.state.items.push(x) followed by setState({items: this.state.items}) leaves the reference unchanged and the update is skipped. Context changes still force a re-render regardless. For function components, the equivalent is memo — which compares props only, since function state already bails out on identical values.
Example
When to use it
- Stop list rows re-rendering when an unrelated part of a large parent's state changes
- Avoid a costly chart or table redraw whose data has not changed
- Replace a hand-written shouldComponentUpdate that only did shallow equality
Frequently asked questions
What is the difference between PureComponent and Component?
PureComponent adds an automatic shallow comparison of props and state and skips rendering when nothing changed, whereas Component re-renders whenever its parent does.
Why is my PureComponent still re-rendering?
Almost always an inline object, array or arrow function prop, which is a new reference on every parent render. A context value the component consumes also forces a re-render regardless of props.
What replaces PureComponent in function components?
memo. It performs the same shallow prop comparison, though it does not compare state, since a function component already skips re-rendering when a state setter receives the same value.
More examples
The inline prop that silently defeats it
Both rows look identical on screen, but only the first ever skips a render. The inline object literal in Bad is a fresh reference on every parent render, so the shallow comparison always reports a change.
import { Component, PureComponent } from 'react';
class Row extends PureComponent {
render() {
return <p style={this.props.style}>{this.props.label}</p>;
}
}
const rowStyle = { fontWeight: 'bold' }; // created once
class Good extends Component {
render() {
// Same object every render: the bail-out works
return <Row label="optimised" style={rowStyle} />;
}
}
class Bad extends Component {
render() {
// New object every render: PureComponent never skips
return <Row label="not optimised" style={{ fontWeight: 'bold' }} />;
}
}
export default function App() {
return (
<div>
<Good />
<Bad />
</div>
);
}The function-component equivalent
memo does the same shallow prop comparison for function components. It does not compare state, because a function component's own setState already bails out when the new value is identical to the current one.
import { memo, useState } from 'react';
const Row = memo(function Row({ count }) {
return <p>Row — count is {count}</p>;
});
export default function Demo() {
const [count, setCount] = useState(0);
const [unrelated, setUnrelated] = useState(0);
return (
<div>
<Row count={count} />
<p>Unrelated state: {unrelated}</p>
<button onClick={() => setCount((c) => c + 1)}>count + 1</button>{' '}
<button onClick={() => setUnrelated((u) => u + 1)}>unrelated + 1</button>
</div>
);
}Mutation makes the update vanish
The mutating button appears to do nothing — the item is in state but the list never redraws. The copying button works. This is the most common bug report against pure components, and it is a mutation bug, not a React bug.
import { Component, PureComponent } from 'react';
class ItemList extends PureComponent {
render() {
return (
<ul>
{this.props.items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
);
}
}
class App extends Component {
constructor(props) {
super(props);
this.state = { items: ['alpha'] };
this.addBroken = this.addBroken.bind(this);
this.addCorrect = this.addCorrect.bind(this);
}
addBroken() {
// Same array reference: the child never re-renders
this.state.items.push('mutated');
this.setState({ items: this.state.items });
}
addCorrect() {
// New array: the shallow comparison sees a change
this.setState((s) => ({ items: [...s.items, 'copied'] }));
}
render() {
return (
<div>
<ItemList items={this.state.items} />
<button onClick={this.addBroken}>add (mutating)</button>{' '}
<button onClick={this.addCorrect}>add (copying)</button>
</div>
);
}
}
export default App;
Discussion