Accuracy with React Memorization
How to use memo in react applications
The idea of returning data that was already stored is not new, but understanding how it works under the hood is very important for all developers.
The memo is memory and that development technic helps to optimize the calculation process for expensive functions. The usage of memorization is a very important practice and is laid as crucial knowledge for development in general. React has good instruments for memorization. But how it works, why it’s important, and how to use it? Let’s find it out in this article in detail with practical examples.
What is Memorization?
Memorization is the function that just has a store. Closure helps to store data and take it from there by key to get the result. Very simple idea: key and value.
Let’s say that we have a function that has two number arguments and the result is an addition of both of those arguments. What do we do? We’ll pass this as an argument this function to the memo function.
With the below function, we’ll create a memory
property as an empty object and then returned the new function which is going to expect arguments. We’ll use those arguments as a key for storing and returning our results from the memorized function.
You can see that after other calls of the memorized function there is a condition where we check the existing results based on keys from stringified arguments. If the result already exists, we just return the result itself.
How to use it?
First of all, let’s put some console.log
in our function to understand better the state changes.
Now let’s create a function for memo:
Time to memorize it and reuse the function multiple times and check how many times the memorized function was called actually.
We created the memorized function and called console.log
five times, but have passed the same arguments in three invocations.
The memorized function will be called only three times because there is no need to call the function again if the result is already known — in which case, the result will be returned from memory.
Applying to a React Component
What happens if we apply this to react component?
Let’s create a very simple component in a React application using yarn create vite
without React.StrictMode
.
Strict mode is not our target. It only makes additional rendering for highlighting potential problems from inherited APIs — for instance, some imported components. We’ll not use any libraries. Instead, we’ll focus on the render process.
Let’s put another console.log
to our memorize
function and run the app.
Then memorize our Block
component using memorize
function:
Let’s try to use the same MemorizedBlock
component several times to make sure that Block
was called only once.
Let’s check the results from our console.log
:
You can see that usage of the MemorizedBlock
was many times but the results from all the times were the same, which means that we calculate it only once. Other iterations were from the memory. The same idea React provides for memorization functionality.
React.memo
Now let’s create more components in order to play with all of them around and check what was updated and at which time.
In each component, let's put console.log
, in order to see what has been rendered. Finally, summarize everything in our application:
Run the application and you have to see in the console the following output:
Now, if you hit on for the button, you will see that all components will be rendered. Why does it happen? Because those components (functions) actually have not been memorized. Let’s wrap Footer
to React.memo
in order to store properties and calculate only stored results from memory as it’s been described before.
And render the app again, and you can see that after hitting the button only two components Header
and Content
has been updated, except Footer
because the Footer
has no changes in props. Let’s do the same with the Content
component.
After hitting the button you will find out that rerenders only Header
because the Header
is not memorized. But as you can see that we got some value in the Content
and it’s not rerendered because the value is always the same.
If check the file react.development.js
for memo
function, there is will be the function:
and comparison happens by the following condition:
It works with twoprevProps
and nextProps
arguments in the additional argument for memo
function like that:
Thus, you can fine-tune the props comparison for component updates. Now add to the content the value:
and memorize the Header
component:
Now after hitting the button you find that only Content
was rendered. It’s because the setCount
same function from the useState
and React care about it. But what if we will create our own function for the handle state, like this:
And update the Header
for function in props:
By updating the state, both components Header
and Content
updating too. It’s because the function setCountHandler
has a referential integrity issue. To have the same reference for the function let’s go to the next part.
useCallback
For fixing the referential integrity issue React has a special hook — useCallback
. Let’s update the handler to
Now updating only Content
because refer to the same for setCountHandler
. But if provide to dependencies the empty object is like this:
The problem will be back because each object has a different reference on each render. To fix that issue, let’s go to the following part.
useMemo
This hook solves the issue with values with references. Literally, it’s returned memorized value. The hook also has a dependency on updating rules.
The difference between useMemo
and useCallback
is that useMemo
returns memorized value, but useCallback
returns memorized function. For example, if you provide value to Context
like this:
The component will keep updating even with a static count value. In this case, useMemo
also will help to solve that issue.
Now the value is memorized and after clicking by buttons nothing happens because the value is always the same. What happens if use state count
right in useMemo
hook?
After the update, nothing gonna change, and the state Content
will be not updated at all. It’s because the dependency useMemo
has not been provided in order to update that value. To fix that, add count from state to dependency:
Now everything works fine. The dependency update the useMemo
correct and value updating updates the Content
component.
Conclusion
Keep in mind that memorization should not happen for light, simple functions. It’s not a big problem if there are no expensive calculations. Only for heavy functions if it is fair to keep that memorization and avoid expensive unnecessary recalculation.
Resources
React.memo
: https://reactjs.org/docs/react-api.html#reactmemoReact.useCallback
: https://reactjs.org/docs/hooks-reference.html#usecallbackReact.useMemo
: https://reactjs.org/docs/hooks-reference.html#usememo