Memoization in React Native

Mohita Prakash
Geek Culture
Published in
9 min readJul 21, 2021

--

If you are a person who writes code and built mind-blowing stuff with a lot of real-time updates to the UI, including animations, gesture controls, giving incredible looks to your UI just like a Disney/Pixar movie then I must say that you have gone through the real pain of being a developer who can do all those magic. Obviously, when I say magic, it does sound like a tap with the magic wand. If it was so, how easy it would have been right? But that’s not the case. You are creating magic, not just using it. Wait a minute!!! Now you guys may wonder why I am giving a lecture about Disney-like stuff by having a story titled “Memoization”. I know I started a little off the track, but guys believe me I wanted all of you to realize that you are doing great work. You deserve a pat on yourself. Do that right NOW !! It’s not easy to understand the concepts in deep and implement them with minimum flaws. I talked about all the above-unrelated stuff to tell you one point that you guys are currently focused on, the performance of your application or optimizing your app. You are here because you give equal importance to your app performance along with your app features. Okay, without any more further adieu, let’s hit our topic !!

The concept of Memoization is not new for the people who work with JavaScript. In simple words, memoization is caching the result of expensive function calls and return the cached result when the function call happens with the same input again. It is important to implement memoization in our app because it can avoid a lot of unnecessary calls to the function if the input is not changing and thus improves our app performance.

In React Native, it is very important to ensure that your code is optimized and the app performance is adequate as we are building mobile apps that users expect to work like a charm. Memoization is one of the optimization techniques to speed up the program.

There are some terminologies you must understand before starting to use memorization in your app. They are listed below. If you already know those you can skip to the next part.

  1. PureComponent:- Pure components in React are based on pure functions in JavaScript. Pure functions are nothing but the functions which return the same output for the specific input parameters. Also, the output shouldn’t have any dependency outside the functional scope. Pure components in React won’t re-render when the state and props get an update with the same values. shouldComponentUpdate() is implicitly implemented in PureComponent class in React. In this, it does a shallow comparison of state and props. If the previous state and props data is the same as the next props or state, the component is not Re-rendered. React.PureComponent is available only for class components. Thus as it restricts rendering it ensures higher performance for the component. Read the below article to know more about PureComponent. https://reactjs.org/docs/react-api.html#reactpurecomponent
  2. Shallow Comparison:- In Shallow comparison the properties of the objects are compared using “===” or strict equality and will not conduct comparisons deeper into the properties. While shallow comparing the primitive values (numbers, strings), it compares their values. When comparing objects, it does not compare their attributes — only their references are compared. So if you shallow compare a deep nested object it will just check the reference not the values inside that object. This may create false-negatives while comparing nested objects. https://reactjs.org/docs/shallow-compare.html
  3. Rendering:- Rendering happens in React whenever the state of a component get updates. Rendering is the process in which the React will collect all the information including current state, props and the desired changes in the UI. It will invoke the functional component and if it is a class component, it will call the render() method. The virtual DOM hasn’t changed in this process yet. Generally, there can be a misunderstanding that rendering is the process of syncing the changes to the DOM. But it is not.
  4. Reconciliation:- The virtual DOM (VDOM) is a programming concept where an ideal, or “virtual”, representation of a UI is kept in memory and synced with the “real” DOM by a library such as ReactDOM. This process is called reconciliation. It is the process of calculation and comparison of the changes to be applied to a virtual DOM.
  5. DOM manipulation:- Once React finishes the calculations that are needed to apply in the application tree, using react-native package it will apply all the required changes to the DOM. These changes are applied synchronously and the DOM is updated. This is called DOM manipulation. This happens in the commit phase.
  6. Re-rendering:- After the first rendering, a second or subsequent render to update the state is commonly referred to as a re-render. A re-render can be caused under any of the following three circumstances: when a prop in a component gets updated, when a state in a component gets updated and when a parent component’s render method gets called.

“When you use React, at a single point in time, you can think of the render() function as creating a tree of React elements. On the next state or props update, that render() function will return a different tree of React elements. React then needs to figure out how to efficiently update the UI to match the most recent tree.”

Now we are familiar with all the terms related to Memoization. It caused a little bit of deviation from our main topic, but trust me guys, understanding those concepts is very important while developing an application.

In this section, we will discuss 3 elements that are related to Memoization. I assume you are using functional components in your app. Because the below-mentioned hooks won’t work for class components. First, I will explain what are these components and then will talk about example code.

  1. React.memo():- React.memo is a higher-order component that optimizes functional components performance. Its functionality is similar to React.PureComponent, but this one is for functional components. That is if the functional component is supplied with the same props and if its output what it renders is the same, then we can skip re-rendering of this component every time. This is taken care by React.memo(). It helps in a fast rendering by memoizing the result. React will skip the rendering process for the component and doesn’t perform a virtual DOM difference calculation, it will just re-use the last rendered result. Like said in the beginning React.memo() does shallow comparison like PureComponent. So if our component is having any nested data structure as a prop, then we will need to write our own comparison function and pass it to React.memo().
  2. useCallback hook:- This hook is also used to prevent unwanted re-rendering in our code and thus make it more faster and efficient. Sometimes we may have to use a combination of React.memo and useCallback. As I mentioned earlier React.memo uses shallow comparison to find the difference between the previous prop and the next prop. This works perfectly fine for the primitive types. But if the functional component is having an array, object, or even callback function as its prop, then the shallow comparison gives false negatives. Thus when the parent component re-renders, every time new callback functions get created even if we have wrapped our component with React.memo. Here the useCallback hooks come for the rescue. We have to wrap our callback function using useCallback and pass the dependencies. So whenever the function call happens, useCallback hooks will return a memorized function. It will create a new function only if the dependencies change.
  3. useMemo hook:- This one is similar to useCallback. But instead of memoizing a callback, it memorizes any value type. We have to pass the dependency list to the useMemo as well. So whenever the dependencies change it will call the function again and do a recalculation and memoize new value.

OK, now it’s too much theory we have talked about. Let’s see some code and understand how to use each of the hooks for optimizing our code.

Case:- 1 — Without Memoization

This example app has a single screen with 2 components. One is the Parent Component and the other is the child component. We will see what happens to the child and parent component when the state of the parent changes and we need to do some UI updates in the Parent component but not the child.

Now below if you check the console, you can see the child is also getting re-rendering for every click in the Parent. But why? It is redundant right. Because there is no UI update for the Child Component. Then there is no meaning of it getting re-rendered. Unfortunately, this is React default behavior. All the components in the Parent tree will re-render if there is a state update in the Parent.

Case:- 2 — With React.memo

Now we will wrap the ChildItem with React.memo() and will see what happens. Like discussed earlier, it will trigger a shallow comparison of the current and previous prop, if any prop is changed then only the ChildItem will re-render.

Now you see the magic. Only the parent is getting re-rendered.

Currently, if you see the ChildItem we are passing only a primitive value as prop. In the next case, we will pass a callback function, and let’ see what happens.

Case:-3 — With Callback function as a prop

I have added a callback function here. It will give an update to the Parent component that how many times the child button has been pressed.

The above is the logs when we press only the button in the Parent component. If you see the logs, you can see that again the childItem is getting re-rendered although we have wrapped it with React.memo(). This is because the shallow comparison will return false for the current and prev props as we have a non-primitive value as a prop. In the next case, we will see how to fix this.

Case:- 4 — With useCallback() hook

To solve the above issue as discussed earlier, we will wrap our callback function with useCallback. Thus useCallback will cache the function and create only a new function when its dependency changes.

Now if you see the logs of parent button clicks, it will re-render only the parent component.

Case:-5 — With useMemo() hook

As discussed earlier, the difference between useMemo and useCallback is that useMemo memoize the result of the function instead of the function itself.

In this below code without useMemo, if you see the calculateSum() function is getting called every time when the component re-render. But imagine if the function is huge. It will affect the performance of the app if the function gets called unnecessarily for each render.

The below code shows the use of useMemo hook for the same input for a function call. You can see in the logs the function gets called only once. Yes!! We have achieved optimization.

You can find the entire source code in the below git repository.

Uff…!! That was pretty huge right. But that’s ok. You have learned very one important concept in react native app development. Make sure you follow this while writing your next functional component, if necessary.

I would say the memoization gives us a huge optimization way, but with a compromise in the RAM usage. So make sure you implement this in your code only if it is very very necessary and you can see the improvement of the app performance after adding this.

That’s it for now. If you like this article shower me with some claps so that it will be a huge motivation for me to do the next articles as well as let this reach the needy. Also ‘Follow Me’ for further updates on the articles.

Stay Safe…Stay Hungry..!!

--

--

Mohita Prakash
Geek Culture

Mobile Application Engineer | Talks about tech, finance and fitness | Believes in simplicity | Day Dreamer