Accuracy with React Memorization

How to use memo in react applications

Anton Kalik
ITNEXT

--

Accuracy with React Memorization
Thanks for the picture of fahrulazmi from unsplash.com

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.

How works memorization for functions
Memorization Schema

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.

Memorize 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.

Logs for results

Now let’s create a function for memo:

Function for Memo

Time to memorize it and reuse the function multiple times and check how many times the memorized function was called actually.

Logs from memorized function

We created the memorized function and called console.log five times, but have passed the same arguments in three invocations.

Results from memorized function

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.

Console for Key

Let’s put another console.log to our memorize function and run the app.

Block Component

Then memorize our Block component using memorize function:

Memorized Block

Let’s try to use the same MemorizedBlock component several times to make sure that Block was called only once.

React Application

Let’s check the results from our console.log:

The result from Memorized React Component

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.

Components

In each component, let's put console.log, in order to see what has been rendered. Finally, summarize everything in our application:

App

Run the application and you have to see in the console the following output:

Output in Console

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.

Footer

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.

Content with memo

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.

Value for Content Component

If check the file react.development.js for memo function, there is will be the function:

Memo in React development files

and comparison happens by the following condition:

Compare function in React development files

It works with twoprevProps and nextProps arguments in the additional argument for memo function like that:

Usage of props comparison

Thus, you can fine-tune the props comparison for component updates. Now add to the content the value:

Content value

and memorize the Header component:

Header in memo

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:

App with function handler

And update the Header for function in props:

Updating Header component for function from 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

Usage of useCallback

Now updating only Content because refer to the same for setCountHandler. But if provide to dependencies the empty object is like this:

Dependency for useCallback

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.

Usage of useMemo

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:

Value for Content

The component will keep updating even with a static count value. In this case, useMemo also will help to solve that issue.

Value in useMemo

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?

Value in useMemo from useState

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:

Updated dependency for useMemo

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

--

--