Improving Performance in React and Redux Applications - Some Quick Wins

Intro
Martin McKeaveney
Software Engineer @

The bottleneck
When a component calls `this.setState()`, React re-renders the DOM in two stages:
-
React’s internal Virtual DOM is re-rendered -
The diff between the previous virtual DOM and current DOM is calculated and changes are applied to the actual DOM
If the first step takes too long, re-rendering will be slow.

Quick Win One - Avoid Unnecessary Re-renders
This is the core principle of pretty much all React performance optimisations.

-
Allows you to specify if a component renders based on the boolean returned. -
Default implementation always returns true. -
Pure components use shallow compare and shouldComponentUpdate. -
Be careful with this! -
as of React 15.3, React.PureComponent is a new base class that replaces the need to use Shallow Compare plugin or the Pure Render mixin.
ShouldComponentUpdate & Pure Components

Higher Order Components
-
We can use HOCs to implement the previous optimisations -
HoCs are functions that accept a base component and return a new component with additional functionality -
Cleaner syntax -
Nice utilities with Recompose

Quick Win Two - Redux and connect()
-
connect() is a HOC
-
Takes the redux store as context
-
The connected component is only rendered when the relevant values are changed (shallow compare) - BUT
-
BUT -
-

Enter Selectors
-
Works like a cache (think memoize in lodash) -
Selectors can compute derived data -
Only recompute when one of their argument functions returns a different value. -
If changed the transform function is then called, otherwise return the previously computed value. -
Act as composable functions that can be used in other selectors

Quick Win 3 - The Pure Render Antipattern
-
tl;dr. The anti-pattern is creating new arrays, objects, functions or any other new identities during render or in Redux connect(mapState). -
In Javascript, different instances have different identities. -
Shallow equality will produce false and tells React to re-render the components. -
ie. [] is equivalent to new Array();

The Pure Render Antipattern (cont.)
Functions

Arrays

The Pure Render Antipattern (cont.)
We can also avoid binding inside the component by binding in an higher-order component like recompose.withHandlers() or redux.connect()

- Timeline/Performance tab in Chrome DevTools
-
Records time executing code and time rendering -
React Render included in code execution time -
The actual DOM render is included in the rendering part of the timeline -
You can also make use of the Redux Devtools slider to time your actions and record them. -
Look for long total times and short self-times. This usually means a React component issue.

Debugging/Profiling
Chrome DevTools Profiler
Debugging/Profiling
React Perf


To Summarise/Other Tweaks
- Only optimise when you really need to
- Keep render() slim (Think functional)
- Keep comparisons shallow (Immutability)
- Build for production and use all the plugins - Uglify, define etc.
- Stateless (functional) components are not any faster than the pure Stateful class (Right Now)
- Normalise your state, prefer objects by ID as opposed to large nested arrays - O(1) as opposed to O(n).
- Rendering in React 15 is roughly 25% faster compared to 0.14
- Pure components are the fastest. Use shouldComponentUpdate
- Rendering in development mode is 2–8x slower than rendering in production
- Rendering in development with React 15 is about 2x slower than 0.14
Thanks!
Questions?
Links
https://github.com/acdlite/recompose https://github.com/reactjs/reselect
https://facebook.github.io/react/docs/perf.html
Improving Performance In React/Redux Applications
By Martin McKeaveney
Improving Performance In React/Redux Applications
- 782