React.memo method and children component re-rendering
In order to avoid a child component to re-render due to its parent re-render when its own state and its received props have not changed, a solution is to wrap it in React.memo. This memoized version of the component will usually not be re-rendered when its parent component is re-rendered as long as its props have not changed.
The React legacy advise us to consider that this method only exists as a performance optimization, not a guarantee. Do not rely on it to “prevent” a rendering, as this can lead to bugs because React may still re-render the component.
When to use the React.memo method?
The React.memo method is used when the component:
- is a functional components only,
- given the same props always renders the same output,
- is generally provided with the same props during re-rendering,
- contains a decent amount of UI elements to re-render.
When not to use the React.memo method?
The React.memo must not be used when:
- the component is not a functional component (use shouldComponentUpdate or PureComponent as seen in the previous article),
- the component is not heavy (less time to re-render than to compare…),
- the props are generally different because time is lost when comparing props with the areEqual function and the code is more complex (lost of time when thinking and writing, lost of time when another developer has to understand the code…).
Trick: When all the props but one are generally the same, you could create a new child component (child component of the child component) containing all the “not changing props” and memoize this new created child component before inserting it in the previous component. The one changing prop is kept in the previous component.
How does the React.memo method works?
In fact, React.memo combines both PureComponent and shouldComponentUpdate in itself when it comes to props.
The React.memo method:
- is a higher order component,
- memoize the result,
- checks for prop changes,
- skip rendering the component, and reuse the last rendered result if props are the same.
As React.memo only checks for prop changes, if the function component wrapped in React.memo has a useState, useReducer or useContext hook in its implementation, it will still re-render when state or context change.
How to implement the React.memo method?
Implement the React.memo method step by step
- Declare the parent component.
- Declare the child component.
- Insert the child component in the parent component.
- Below the child component, declare a constant whose name is ChildComponentNameMemo.
- In the constant, call React.memo and pass it:
- the child component submitted to ShouldUpdate or not (Remark: pass the child component name if already declared above, or insert directly all the child component inside instead of declaring it outside),
- optional: an areEqual custom comparison function containing a specific rendering condition (Remark: if React.memo must only check on props change, there is no need to pass this second argument).
- If a function areEqual has been passed in the React.memo method, declare it above the just declared constant wrapping the child component. The function receives two arguments: prevProps and nextProps and return the condition.
- Note: Be careful, contrary to the shouldComponentUpdate method, the areEqual function returns true if the props are equal and false if the props are not equal.
- Example: when writing a specific condition which is true does correspond to when the component must NOT be re-rendered. I explain: if you want the component be re-rendered when the result is even, the condition must be true when the result is not even (is odd).
- In the parent component, replace the child component name by the Memoized child component name.
That’s all!
Code structure when React.memo just has to check props
First solution – insert all the child component code in the React.memo method:
Second solution – call the already declared child component
Code structure when passing a specific conditional rendering function
In a Create React App, the last line of code should be replaced by export default React.memo(MyComponent, areEqual);
Updating: Code structures modified since the React 18.2 version
Since the 18.2 version the structure has been a little bit updated:
- First of all,
import { memo } from 'react';
the same way as you do for the hooks. - Then replace
React.memo
bymemo
only in the MemoizedComponent constant.
No so big changes…
Avoid re-rendering when props are an object, array or function
Why does the component still re-render when props are an object, array or function?
In JavaScript, object or array are not equal even if the individual elements are each the same. So, if object or array are created when rendering the parent component, React will still consider it to be changed after comparison. That means that a memoized child component will be never consider the same if it receives an object or array a s a prop.
In JavaScript, a function () {} or () => {} always creates a different function, similar to how the {} object literal always creates a new object. So, React will consider new function created when rendering the parent as to be changed even if the definition of the function is the same. That means that a memoized child component will be never consider the same if it receives a function a s a prop.
Fortunately, there are solutions to avoid this:
- simplify props,
- memoize props in the parent component.
What are the solutions if the prop is an object? :
- memoize props in the parent component with useMemo() if the prop is an object (useMemo() will be studied in a next episode);
- better solution: pass individual values as props instead of a whole object (example: instead of passing an array named person, pass the name, the age… of this person individually);
- another possible solution in some situation: use a boolean controlling the presence of a value rather than the value itself.
What are the solutions if the prop is a function? :
- declare it outside the component so that it never changes;
- use the hook useCallback to cache its definition between re-renders (useCallback will be studied in a next episode).
Write your first optimized re-renders child components with the below example
This example is based on a freeCodeCamp challenge using the shouldComponentUpdate() method in a class component I have adapted to functional components and React.memo.
More information about the App we are going to code
In this example, there are two components : Controller and OnlyEvens.
- The Controller parent component:
- has a state property named value which is initialized with 0;
- contains a button which increment the state value by 1 every time it is clicked;
- contains a child component named OnlyEvens which receives a value (the state value property value) as prop;
- re-renders every time its state is modified.
- The OnlyEvens child component:
- renders the value received as props in a paragraph;
- should updates only if the value of its new props is even.
Let’s code the App step by step
Code the optimized re-renders App step by step
We consider that React as been installed or a new pen as been created on codepen.
- Create the HTML file, the element and add it the optimize id attribute to “attach” the React App (Controller component) rendering.
- Create the Controller component, declare its local state value property, its updating function setValue and initialize the value with 0.
- In the render, insert four elements:
- the button whose text is “Add”, on which is assign a onClick event which trigger the addOne event handler function(to be created),
- a paragraph containing the “Number of clicks: ” text and displaying the state value,
- a paragraph containing the “Memoized component:” text,
- the OnlyEvens child component (to be created) which receives a prop named value whose value is the state property value.
- Declare the addOne function which update the state property value by adding 1.
- Create the OnlyEvens child component which renders the received props value and the text “is even!” or “is odd!” depending on the received value, in a paragraph.
- Test the App which should display the value and the corresponding text every time the button is clicked.
- Memoize the the child component by declaring a constant whose name is OnlyEvensMemo. Call the React.memo method and pass it OnlyEvens and areEqual as arguments.
- Declare the areEqual function above. Pass it prevProps and nextProps as arguments and make it return true when the nextProps is not even
next.value %2 != 0
. - In the parent component, Controller, replace the child component name, OnlyEvens, by the memoized child component name, OnlyEvensMemo.
- Click on the add button in the App: the number of clicks is re-rendered every time (means the parent component is re-rendered after every clicks), the memoized component is re-rendered only when the click number is even (the “is odd!” text will never appear, the odd numbers will never appear).
That’s all. Find the final code below.
The final code of the optimized re-renders App
In the pen inserted below, I have added:
- two elements to compare memoized and not memoized children components:
- a paragraph whose text is “Not memoized component:”,
- the OnlyEvens child component (the not memoized one, the original one).
- some console.log to check everything is running well:
- inside the OnlyEvens component displays ‘OnlyEvens re-renderded’ and the number of clicks,
- inside the Controller component displays ‘Controller re-renderded’ and the number of clicks,
When clicking the “Add” button and watching the order of events in the browser’s or pen console, :
- “Controller re-rendered” should appear every time the button is clicked,
- “OnlyEvens re-rendered” should appear once on odd number (only the not memoized component) and twice on even number (both memoized and not memoized components).
Play with the below pen
See the Pen
React tricks: Optimize functional component Re-Renders with React.Memo by Coding-Tricks (@coding-tricks)
on CodePen.
Another example
I have coding another example in a pen.
This app contains 2 buttons which change the colour of the children components sentence every time they are clicked.
- One of the button has green as text and modify the current colour of the displayed text in green.
- The second one has blue or red in alternate as text (each time it is clicked the colour name is changed) and change the current colour of the text in blue or red depending on the colour name displayed on the button when it is clicked.
I have inserted twice the same child component: one of them is memoized, the other is not…
Have a look at the pen on codepen! Play with it!
In conclusion
In this article we have learnt how to optimize children components re-renders in a functional component with React.memo.
Other Optimizing performance solutions exist like useMemo(), useCallback(), useRef(), debouncing, throttling… We have already learnt some of them, we will discover others in next articles!
In conclusion
In this article we have learnt how to optimize children components re-renders in a functional component with React.memo.
React.memo is not the last solution to optimize the performance, other solutions exist like useMemo(), useCallback(), useRef(), debouncing, throttling… We have already learnt some of them, others will be discovered in next articles!