
tldr; prefer PureComponent over Component, but never mutate your objects (and use other best practices)
When to use Component or PureComponent
I switched to using PureComponent awhile back on the premise of it being a more performant version of Component. This turned out to be true, but the performance gains come with a few strings attached. Let’s dig in to PureComponent and understand why we should be using it.
Component and PureComponent have one difference
PureComponent
is exactly the same as Component
except that it handles the shouldComponentUpdate
method for you. When props or state changes, PureComponent
will do a shallow comparison on both props and state. Component
on the other hand won’t compare current props and state to next out of the box. Thus, the component will re-render by default whenever shouldComponentUpdate
is called.
Shallow Comparison 101
When comparing previous props and state to next, a shallow comparison will check that primitives have the same value (eg, 1 equals 1 or that true equals true) and that the references are the same between more complex javascript values like objects and arrays.
Never MUTATE
You’ve probably been hearing not to mutate objects and arrays in props and state. If you were to mutate objects in a parent component, your “pure” child components wouldn’t update. Although the values have changed upstream, the child would be comparing the reference to the previous props and not detect a difference.
Instead, return new objects when you make a change by either leveraging es6 for object and array spreading or using a library to enforce immutability.
Are there performance issues?
Comparing primitives and object references is an incredibly cheap operation. If you have a list of child objects and one of the children updates, doing a check on their props and state is lightning fast compared to the cost of re-rendering each one.
Other ways you could slip up
Don’t bind values in functions in render
Say you have a list of items, each passing a unique parameter to parent method. In order to bind the parameter you’ve maybe done something like this:
<CommentItem likeComment={() => this.likeComment(user.id)} />
The problem is that every time the parent’s render method is called, a new function (with a new reference) is created to be passed to likeComment
. This has the side effect of changing the props on each child which in turn will cause them all to re-render, even if the data itself is all the same.
To get around this, only pass the reference to the parent’s prototype method to children. The child’s likeComment
prop will always have the same reference and never cause a needless re-render.
<CommentItem likeComment={this.likeComment} userID={user.id} />
Then in the child component create a class method that will reference its props:
class CommentItem extends PureComponent {
...
handleLike() {
this.props.likeComment(this.props.userID)
}
...
}
Don’t derive data in the render method
Consider a list of articles from which your profile component will display the user’s 10 most liked pieces.
render() {
const { posts } = this.props
const topTen = [...posts].sort((a, b) =>
b.likes - a.likes).slice(0, 9) return //...
}
topTen
will have a brand new reference each time the component re-renders, even if posts
hasn’t changed and the derived data is the same. This will then re-render the list needlessly.
You can solve this by caching your derived data. For example, Set the derived data in the component’s state and update only when the posts
have updated.
componentWillMount() {
this.setTopTenPosts(this.props.posts)
}componentWillReceiveProps(nextProps) {
if (this.props.posts !== nextProps.posts) {
this.setTopTenPosts(nextProps.posts)
}
}setTopTenPosts(posts) {
this.setState({
topTen: [...posts].sort((a, b) => b.likes - a.likes).slice(0, 9)
})
}
If you’re using Redux, consider using reselect to create “selectors” to compose and cache derived data.
Take Aways
It is safe to use PureComponent instead of Component so long as you follow two simple rules: 1) Mutations are bad in general, but the problems are compounded when using PureComponent. 2) If you’re creating new functions, objects, or arrays in the render method you’re (probably) doing it wrong.
Thanks to Daniel Min for translating this into Korean.
✉️ Subscribe to CodeBurst’s once-weekly Email Blast, 🐦 Follow CodeBurst on Twitter, view 🗺️ The 2018 Web Developer Roadmap, and 🕸️ Learn Full Stack Web Development.