Performance
Premature Optimization
Premature optimization is the root of all evil -- DonaldKnuth
Premature Optimization is optimizing before we know that we need to do it.
Recommendation: Get your application working and then near the end of a development cycle take the time to optimize for performance.
render
in React?
What causes a component to A re-render can only be triggered if a component’s state has changed. The state can change from a props
change, or from a call to setState
or a useState
update state function. The component gets the updated state and React decides if it should re-render the component. Unfortunately, by default React is incredibly simplistic and basically re-renders everything all the time.
Component changed? Re-render. Parent changed? Re-render. Section of props that doesn't actually impact the view changed? Re-render.
Summary
- Default Behavior: Changing state
- results in that component and all descendants being re-rendered.
- Default Behavior: Changing state that updates a prop in a child
- results in that component and all descendants re-rendered.
- Override shouldComponentUpdate: return true
- results in that component and all descendants being re-rendered.
- Override shouldComponentUpdate: return false
- results in no re-renders (current component and all descendants).
- Override shouldComponentUpdate: see if props changed and only then return true.
- Change value prop in Parent and child will re-render first time (when value changes) but not subsequent times because value prop remains the same (true).
- PureComponent: comment out shouldComponentUpdate and make ChildA a PureComponent
- Change value prop in Parent and child will re-render first time (when value changes) but not subsequent times because value prop remains the same (true).
Component Render Demo
styles.css
main.js
Wasted Renders
React has two phrases that run sequentially to update the UI.
Render Phase
The "render phase" is where React compares a previous version of a Virtual DOM representing the UI with an updated version to figure out what if any changes need to be made.
Commit Phase
The "commit phase" is where React actually changes the real DOM.
As demonstrated in the Virtual DOM chapter React is very efficient about figuring out the minimal DOM operations to make in the "render phase" and batches them to make rendering the UI extremely performant.
However, the "render phase" does take work and consumes resources and should not take place if it isn't needed. If all the components on the screen are constantly rendering when the don't need to this is a common source of eventual performance problems. We call this problem: "wasted renders".
Wasted Renders can be fixed using:
React.PureComponent
when using class components.React.Memo
when using function components.
React.PureComponent
React.PureComponent
is similar to React.Component
. The difference between them is that React.Component
doesn't implement shouldComponentUpdate()
, but React.PureComponent
implements it with a shallow prop and state comparison.
If your React component's render()
function renders the same result given the same props and state, you can use React.PureComponent
for a performance boost in some cases.
Note
React.PureComponent
'sshouldComponentUpdate()
only shallowly compares the objects. If these contain complex data structures, it may produce false-negatives for deeper differences. Only extendPureComponent
when 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".
React.memo
React.memo
is a higher order component. It's similar to React.PureComponent
but for function components instead of classes.
If your function component renders the same result given the same props, you can wrap it in a call to React.memo
for a performance boost in some cases by memoizing the result. This means that React will skip rendering the component, and reuse the last rendered result.
React.memo
only checks for prop changes. If your function component wrapped in React.memo
has a useState
or useContext
Hook in its implementation, it will still rerender when state or context change.
By default it will only shallowly compare complex objects in the props object. If you want control over the comparison, you can also provide a custom comparison function as the second argument.
This method only exists as a performance optimization. Do not rely on it to "prevent" a render, as this can lead to bugs.
Note
Unlike the
shouldComponentUpdate()
method on class components, theareEqual
function returnstrue
if the props are equal andfalse
if the props are not equal. This is the inverse fromshouldComponentUpdate
.
React.memo
Demo
Run the demo below and open the console to observe some wasted renders.
Steps:
Before beginning the demos in this chapter add the following css class if it doesn't already exist.
styles.css
.box {border: 1px dashed;padding: 30px;}Paste the code below into
main.js
Open the application in a browser.
Open Chrome DevTools and switch to the
console
.Type in the add textbox to add an item and then click the add button.
Notice that every item in the list re-renders even though you only added one item.
Note: Updating or removing an item also causes everything to re-render.
Commment out the
ListItem
component.Uncomment the
ListItem
component below the original wrapped in aReact.memo
function.Refresh your browser.
Once again type in the add textbox to add an item and then click the add button.
Notice that only one item in the list re-renders since the other
ListItem
's are the same. You have successfully eliminated some wasted renders.The same issue of every item re-rendering actually existing when editing or removing an item. We have now fixed all of these wasted renders. If time permits feel free to change back to the non memoized implemention of
ListItem
to see the wasted renders.
React.PureComponent
Demo
Run the demo below and open the console to observe some wasted renders.
Steps:
- Paste the code below into
main.js
- Open the application in a browser.
- Open Chrome DevTools and switch to the
console
. - Type in the add textbox to add an item and then click the add button.
- Notice that every item in the list re-renders even though you only added one item.
- Commment out the
ListItem
component (version labeled a). - Uncomment the
ListItem
component below the which extendsReact.PureComponent
function (version b). - Notice that the anonymous callback functions in the
onClick
event handlers where changed to usebind
so that the same version of the function would be passed as a prop every time instead of a new instance.- <button onClick={() => onEdit(item)}>Edit</button>- <button onClick={() => onRemove(item)}>Remove</button>+ <button onClick={onEdit.bind(this, item)}>Edit</button>+ <button onClick={onRemove.bind(this, item)}>Remove</button> - Refresh your browser.
- Once again type in the add textbox to add an item and then click the add button.
- Notice that only one item in the list re-renders since the other
ListItem
's are the same. You have successfully eliminated a wasted render. - Try version c) of the component which uses the
shouldComponentUpdate
lifecyle method to control whether the component updates and only focuses on theitem
prop and ignores theonEdit
andonRemove
callbacks.
The same issue of every item re-rendering actually existing when editing or removing an item. We have now fixed all of these wasted renders. If time permits feel free to change back to the nonpure implemention of
ListItem
to see the wasted renders.
FAQs
What is memoization?
In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.
Why is my component rendering twice?
Remove the <React.StrictMode>
tag as shown below and this behavior will go away however you may not want to remove it as it doesn't happen in production. For more information, see the Strict Mode Documentation or this stackoverflow question: Strict Mode Rendering Twice.
index.js
Reference
- React.PureComponent
- React.memo
- Performance Tools
- Optimizing Performance
- Profiling Components with Chrome
- Why is immutability so important (or needed) in JavaScript?
- The DAO of Immutability
- Why Did You Render
- Why Did You Render Blog Post
- React Component Renders Too Often
- Flame Chart
- React Rendering Misconception
- You Probably Don't Need Derived State
- When to Re-Render a Component
- How to Update a Component's Props in React
- How to force a React component to re-render
- Pluralsight: Optimize Performance for React (payment required)
- Strict Mode Documentation
- Strict Mode Rendering Twice
- Optimizing React Native