Optimizing Redux Components
In part one of Riipen’s three-week series on how to optimize React components, we explored React’s virtual DOM and antipatterns that spoil the reconciliation process.
This week, we’ll take a look at how you can avoid that process altogether if you already know your component doesn’t need to be re-rendered (for example, if its output only changes when particular properties are modified). We’ll also explore how to apply what we learned last week to components connected to the Redux store.
Once you’ve got your component rendering without creating redundant object references, the next step in optimization is to take a look at what happens in your component before it renders — that is, before React has had an opportunity to reconcile changes. This code may be run every time the state is updated, even if your component is returning a component tree that can be easily reconciled (without the gotchas mentioned in our previous post).
Before we dive in, take a look at the React documentation to familiarize yourself with the component lifecycle.
The Same Rules Apply for State Mapping Functions
If you’re using Redux, be mindful of what connected components are doing before they render. Mapping functions like
mapStateToProps might be doing a lot of work every time the state is updated, even if React ultimately discards the render output of the connected component.
render, avoid creating callbacks and other functions in mapping functions, especially if those variables will be passed in as props to other components. Instead, use
mapDispatchToProps or take advantage of the fact that, by default,
dispatch to your component as a prop. That means you can create component methods that use
this.props.dispatch as a function instead of creating new functions every time the state changes — which can happen quite frequently, especially if you are using a package like Redux Form.
No changes? Skip Rendering Altogether
In some cases, even if React is able to discard the output of
render most of the time, the method still ends up doing a lot of unnecessary work. You can skip rendering entirely by implementing
shouldComponentUpdate. This is a simple method that receives
nextState as arguments and returns
render should be called. If it returns
false, React won’t render the component or reconcile the result with what has already rendered.
In most cases, in a Redux app,
shouldComponentUpdate compares only
nextProps because any variable state changes have already been selected for the component in
It’s best to implement
shouldComponentUpdate only if you are very certain about the conditions under which your component needs to render. For example, if you are outputting a list of entities and know that your component only changes when they are modified in the Redux store, you can implement this method with confidence that your component won’t be outdated by other state changes.
- Your implementation needs to be speedy, ideally doing a direct comparison of specific properties you know to be variable; otherwise, you could end up bogging down the component in your effort to optimize it!
- A good rule of thumb is for your implementation to be more no more complex than
PureComponent.shouldComponentUpdate, which does a shallow comparison on
- If your component can make use of
shouldComponentUpdate, move as much logic as possible into the render function so that it can be avoided if the component doesn’t need to update.
- Be aware that future versions of React may interpret the results of
shouldComponentUpdateas a hint rather than a directive.
React.Component implements a default
shouldComponentUpdate method that always returns
PureComponent implements a shallow prop and state comparison. It can be a shortcut to a custom
shouldComponentUpdate implementation, but the same rules apply as to general reconciliation:
mapDispatchToProps can easily foil the comparison by passing in new references to the same data on every call.
That said, using a library like Reselect, that memoizes Redux selectors, will prevent new references being created from simple data selections.
PureComponent should be the conclusion of your optimization rather than the starting point. Defaulting to it will actually slow down your application because React could end up doing a double diff on every render: first of the
state and then (if they are different) of the virtual DOM returned by
If you decide to extend from
PureComponent, have a clear idea of what property and state changes you need to watch out for and why a custom implementation of
shouldComponentUpdate would be redundant.
Skip Redux Property Mapping
Just as React lets you skip render with
shouldComponentUpdate, React Redux allows you to skip
connect decorator accepts
options as a fourth parameter, which is an object that can have the following properties:
Each function is of course optional and simply returns
false. See the React Redux documentation for details on how to use these options.
mapStateToProps function does something more complex than simply select data from the store, and which you can’t move to the
render function, consider implementing one of these tests. In the vast majority of cases, however, they shouldn’t be necessary.
Although optimizing Redux applications can seem overwhelming at first, the same principals apply to them as regular React apps. Having an understanding of what’s happening behind the scenes yields a few simple things to be mindful of. And, with judicious use of hooks like
shouldComponentUpdate, you can always work around the default behaviour to speed up more complex code.
In the final part of this series published next week, we’ll take a look at some tools you can use to determine what components in your app would most benefit from these techniques and how you can measure the effectiveness of your optimizations.