When to use React's memo HOC
When React wants to render a component, it renders all of the components in the tree from that point. So if you have a parent component with two child components, a re-render (↻) of the parent will include re-rendering both children.
Parent ↻
├── ChildA ↻
└── ChildB ↻
Since data typically flows down the tree, this is the behavior that you probably want for the UI to reflect the current state of the application.
However, there may be instances when you do not want a component to re-render when its ancestor re-renders. Let’s imagine a similar scenario with two children. One of the components is presentational while the other has a deep tree of its own.
Parent
├── BasicChild
└── ComplexChild
├── GrandChildA
└── GrandChildB
└── GreatGrandChild
Just like before, any changes (✴) to the parent will result in re-rendering all if its children – including the complex subtree. Depending on the depth of the tree or the complexity of the render, this could be a time-consuming render cycle.
Parent ✴↻
├── BasicChild ↻
└── ComplexChild ↻
├── GrandChildA ↻
└── GrandChildB ↻
└── GreatGrandChild ↻
We want to prevent ComplexChild
from re-rendering unnecessarily to improve the performance of our application.
Thankfully, React has some features to accomplish this.
Class components
Class components have a shouldComponentUpdate
lifecycle method that allow you to determine whether a component should re-render based on props and state.
class PreventRerender extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return this.props.value !== nextProps.value;
}
}
PureComponent
goes one step further by implementing shouldComponentUpdate
with a shallow prop and state comparison. When you component extends PureComponent
, it will only re-render if its props or state have changed.
class PreventRerender extends React.PureComponent {}
Function components
For modern function components with no lifecycle methods, we need a different solution. React exports a memo
higher-order component (HOC) that takes a function component and returns a wrapped component that will only re-render if a shallow comparison of props indicates a change.
const PreventRerender = React.memo(function PreventRerender(props) {});
For more customization, you can pass an optional compare function to React.memo
as a second argument that determines whether to render based on previous and next props.
Any of the strategies above to prevent unnecessary re-renders come at a small cost, so make sure to implement them only when necessary. You can use the React DevTools’ Profiler panel to determine which areas of the React tree are good candidates for performance improvements.
In our example above, we could wrap our complex component with React.memo
in order to prevent re-rendering when the props are unchanged:
export default React.memo(ComplexChild);
We would not need to do anything to the basic child component, as any memoization or lifecycle checks would probably be more expensive than the cost of re-rendering.
With that change in place, a change within the parent component would only result in re-renders for Parent
and BasicChild
– the expensive tree in ComplexChild
would not change.
Parent ✴↻
├── BasicChild ↻
└── ComplexChild
├── GrandChildA
└── GrandChildB
└── GreatGrandChild
I hope that helps explain when and how to use React’s memo
HOC.
Happy memoizing!