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.
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.
Below we have
Table functional component with two separate states,
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
Buttoncomponent 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.
In order to prevent these costly and unnecessary re-renders, all we have to do is use
memo on the
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
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
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
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
useContext hooks then it will render as expected.
In Part 2 I will discuss