Memoization in React with Functional Components: Part 1
React is a lightweight frontend library with many features to optimize performance. One of such features available in React is memoization. Memoization is defined as
“…an optimization technique that makes applications more efficient and hence faster. It does this by storing computation results in a cache, and retrieving that same information from the cache the next time it’s needed instead of computing it again.” ²
Thinking in terms of component based react development, think of “computation results” as functional components. Memoization is possible with class components as well, but this article will only cover functional components.
Use Case
A common use of memoization in React is preventing a child component from re-rendering when it does not need to. For example, if a parent component updates some state value that does not involve the child, by default React will re-render the child component. Using memoization however, we can restrict the re-rendering of child components to reduce unnecessary load.
The Problem
Below we have Table
functional component with two separate states, count
and buttonData
. The button that changes the count
state exists in the parent component, and that state is not passed to any of its children. For this reason, we do not want Button
to re-render when count
is changed, because they do not have anything to do together.
Here is the custom Button
component that the Table
component renders.
Important: note that the props being passed to Button
are destructured. When memo
compares previous and new props, it only uses a shallow comparison, so a props
object will always cause a re-render. This is because additional overhead is required to compare a complex data structure.³
As this code stands, every time the count
state is updated in the Table
parent component, the Button
child component will re-render. This is a waste of computation and can be prevented to increase performance.
The Solution
In order to prevent these costly and unnecessary re-renders, all we have to do is use memo
on the Button
component.
As noted above, this memoizes the Button
component, so that every time it is attempted to re-render, it will first compare the last set of props passed to the current set of props passed. Since both id
and label
have not changed at all, the component does not need to re-render.
Now, whenever you change the count
state in the parent Table
component (or any other state unrelated to buttonData
for that matter), the child Button
component will compare it’s previous and current props, and decide not to re-render.
When You DO Want to Re-render
Assuming you only want to re-render the child component when its own state changes, all you have to do is update the state correctly. The example bellow will not re-render the child component.
Note line 22. What we are doing here is creating a copy of state, changing one property on one of the objects within the state array, and setting state with that same array. The useState
hook see’s this as updating state with its old state, and therefore will not trigger a render. The very simple solution to this is below.
The solution here is to use the spread operator, “spreading” the array into a new array. This will trigger the parent component to render, passing new props (id and label) to the child Button
component. memo
will compare the old props with the new, and since now the props have actually changed, the component will render again.
One other thing to note here, is that only the Button
component with the changed props values will be rendered, not the entire list of Button
s.
Keep In Mind
Memoizing a functional component only compares the props that are sent to the component to determine rendering. If the Button
component itself has state or context, utilizing the useState
, useReducer
, or useContext
hooks then it will render as expected.
In Part 2 I will discuss useCallback
, useMemo
, and useRef
.
GitHub:
https://github.com/AidanMcB/Memoization-in-React/tree/memoization-part-1
Resources:
- https://reactjs.org/docs/react-api.html#reactmemo
- https://www.freecodecamp.org/news/memoization-in-javascript-and-react/#:~:text=In%20programming%2C%20memoization%20is%20an,instead%20of%20computing%20it%20again.
- https://blog.openreplay.com/improving-react-application-performance-react-memo-vs-usememo/#:~:text=memo%20utilizes%20shallow%20comparison%20by,the%20worse%20the%20overhead%20is.