When to use Component or PureComponent
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 theshouldComponentUpdate
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:
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.
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) } }
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.
Comments
Post a Comment