
statethisPureComponent or React.memo when you can (and, in general, make your components depend only on props and state when you can)We're going to compare these things:
in these ways:
biased by:
class SimpleClassComponent extends React.Component {
render() {
return <div>{this.props.name}</div>
}
}
class SimplePureComponent extends React.PureComponent {
render() {
return <div>{this.props.name}</div>
}
}
const SimpleFsc = ({ name }) => <div>{name}</div>
const SimpleMemo = React.memo(SimpleFsc)Component and PureComponent are pretty easy to read and nearly identical (and, pre 16.8, are your only option if you want state) but FSCs are simpler in terms of being able to avoid the this keyword and removing the class boilerplate that comes with an ugly polyfill if you're transpiling to ES5.
Okay this should sound like an obviously bad thing that you should try to avoid.
Consider this incredibly contrived example:
class Pure extends React.PureComponent {
render() {
return this.props.foo.bar
}
}
class PureWrapper extends React.Component {
constructor(props) {
super(props)
this.foo = { bar: 1 }
}
render() {
this.foo.bar += 1
return <Pure foo={this.foo} />
}
}In this case, Pure will not rerender because the prop foo hasn't changed (it's the same object in memory). This example is quite ridiculous, but you can run into this with something like Redux if you mutate your global state.
To avoid this, the React documentation suggests:
React.PureComponent'sshouldComponentUpdate()only shallowly compares the objects.
If these contain complex data structures, it may produce false-negatives for deeper differences.
Only extendPureComponentwhen you expect to have simple props and state, or useforceUpdate()when you know deep data structures have changed.
Or, consider using immutable objects to facilitate fast comparisons of nested data.Furthermore,React.PureComponent'sshouldComponentUpdate()skips prop updates for the whole component subtree.
Make sure all the children components are also "pure".
Having a PureComponent render children "inline" sidesteps any optimizations you would normally expect:
class Pure extends React.PureComponent {
render() {
return this.props.children
}
}
const PureFsc = React.memo(({ children }) => children)
class PureWrapper extends React.Component {
render() {
return (
<>
<Pure>
<SomeOtherComponent />
</Pure>
<PureFsc>
<SomeOtherComponent />
</PureFsc>
</>
)
}
}Here, <SomeOtherComponent /> is sugar for React.createElement('SomeOtherComponent') which returns a new object every time. So in Pure and PureFsc, the children prop is changing (shallowly) every time!
To avoid this, consider caching the children or creating a new pure component that renders the component and its child.
FSCs have a minor gotcha involving rendering PureComponent children.
If an FSC is passing a function to its PureComponent children as props, odds are it's recreating that function every render and the pure children have no way of successfully performing a strict equality on props.
Consider the following setup:
class Pure extends React.PureComponent {
render() {
return this.props.fn()
}
}
class PureWrapper extends React.Component {
fn = () => <div>Foo</div>
render() {
return <Pure fn={this.fn} />
}
}
const PureWrapperFsc = () => {
const fn = () => <div>Foo</div>
return <Pure fn={fn} />
}Here, PureWrapper, on each render, is passing the same fn to Pure so Pure, being a PureComponent, will not bother calling its render() function. PureWrapperFsc, on the other hand, creates a new function to pass to Pure every render and so Pure will also render every time.
The easiest way to avoid this problem is to use a class component, but if fn doesn't depend on props, you can define it outside your component:
const fn = () => <div>Foo</div>
const PureWrapperFsc = () => <Pure fn={fn} />All in all, FSCs are the cleanest solution for simple components but regular React Components can have less unexpected side effects.
"The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming." - Donald Knuth
Now that we have that out of the way...
The main power of PureComponents is that they will avoid their render() function if they receive the same props and their internal state hasn't changed.
Let's check out these following examples.
Here, we're rendering a regular React Component:

Notice the console indicating the component's render() function being called on each click.
Now let's see the same setup with a PureComponent:

Notice how we don't see any logs for subsequent renders?
That's because PureComponents implement the shouldComponentUpdate lifecycle hook to shallowly compare their new props and state to their old props and state.
Because these haven't changed, shouldComponentUpdate() returns false and render() is not called - the previous DOM markup is instantly returned.
* Note the results are the same for FSCs vs React.memoized FSCs.
So in general, we expect big savings when using a PureComponent or React.memoized FSC and we're either rendering many components, or components whose render() function is slow, or a deep tree of components. Let's see what happens!
We're going to follow this repo for comparing components.
We can mess with a few things:
For the following comparisons:
setState that starts the render and then in componentDidMount. They are an average over 10 rendersAs expected, the render times for non-pure Components are nearly identical regardless of props mutation.
For PureComponent and React.memo, we see huge performance savings when rendering lots of complex components whose props aren't changing.
That makes sense - if render() takes a long time and we can skip all that work, we'll see big gains.
We also see a slight performance boost when choosing functional components over class components.The only surprising situation here occurs when passing lots of props.
It takes roughly 600ms just to pass 1000 props to 1000 children, never mind shallowly compare them.
Given that, we do see a slight performance hit when using PureComponent and React.memo because the work of prop-equality-checking is more than the work of render().
PureComponent and React.memo when properly applied.Learn more about how The Gnar builds React applications.