Memoization to improve UI performance in ReactJS

Memoization to improve UI performance in ReactJS

Every developer strives to improve the performance of the apps they build. Users enjoy fast and responsive apps. Delays in UI can reduce the interest of users in your app.

There are many types of performance improvement and also many ways to improve performance. In this blog, I'm going to describe some memoization tips specifically to improve User Interface performance in ReactJS.

React offers a wrapper or higher-order component React.memo(). When we wrap our react component with React.memo() , React memoizes the props of rendered output and skips unnecessary renderings to improve UI performance. Let's go a bit dipper in it...

What is Memoization?

In computation terminology, memoization is kind of similar to caching. We store the results of some function calls and return those results when the same input occurs again, without recomputing it.

In reference to React, we'll store the props of rendered component and when re-render is going to occur we'll first compare the current props with memoized props and if they are the same then we'll skip re-rendering of that component.

React.memo()

React.memo() can only be used by pure functions/functional components and hooks. For class-based components, we have to use React.PureComponent.

Memo performs a shallow comparison between props of the component and determines whether to render or not. We will see an example now to get a clear vision of memoization.

Note: if the state of a component changes whether, with the same or different props, it is going rerender whether you use memo or not.

For example, Let's create a simple and normal functional component Card which accepts name as a prop.

const Card = (props) => {
    console.log("rendered normal card");
    return (
        <div>
            <p>{props.name}</p>
        </div>
    );
};

Also, create another card component with the same prop structure but wrap it inside React.memo

const Card = (props) => {
    console.log("rendered memoized card");
    return (
        <div>
            <p>{props.name}</p>
        </div>
    );
};

export const MemoCard = React.memo(Card);

There is also a counter variable which gets incremented on every button click. All are wrapped inside the App component. The same value to the name prop is passed in both cards. Now let's see what happens.

Open the console of the below sandbox and try to increment the counter.

Did you spot the difference?

The normal card was getting rendered on every click while the memoized card was only rendered once in beginning.

Now, you can get the purpose and importance of doing memoization. Even if the prop of the Card component is not changing and it has nothing to do with the counter, it still gets rerendered. If we are making a large application then such renderings can lower the performance of the app which is not a good practice.

Customization in props comparison

By default memo does a shallow comparison of props. But we can also customize the comparison by passing an argument to memo.

Suppose, we do not want to re-render the component for a certain condition on props. For that, we create a function inside which we write the comparison logic and pass it to the memo.

Note: This function must return true when we want to skip re-rendering

...
function compareProps(prevProps, newProps){
     return prevProps.name===newProps.name;
}

export const MemoCard = React.memo(Card, compareProps);

Here, when the name of previous props and new props are the same, the component will not get re-render.

You can try !== condition in above function and see that now it gets re-rendered on every click

Something to note

Whenever the parent component gets re-render, it also creates new references to the functions defined inside it with the same code. So, if we are passing such function as a prop to a memoized component then that component will get re-rendered.

Why so?

function printSomething(){
      console.log('Something');
}

const f1=printSomething();
const f2=printSomehting();

In the above example if we compare f1 and f2 then it will return false even though they point to the same function. This is what happens in the scenario that I described earlier. Prop comparison return false and component get re-rendered.

To avoid this, we can use useCallback(). It preserves the function instance even after re-rendering. Let's see this in our old example.

In the above code, uncomment the samplefn and CompWithCallback, refresh the browser and open the console. You will see log statement function wrapped with callback gets printed only once even if you click multiple times but if you remove the useCallback from the samplefn and rerun the app then everytime you click the button log statement gets printed.

The same scenario can happen when we pass an object as a prop. But for an object, we can't use useCallback. Instead, we can use the custom prop comparison which we discussed earlier. Compare the exact values inside of the object in this function.

Example:

...
// we pass a person object as a prop to component
function compareProps(prevProps, newProps){
     return prevProps.person.name===newProps.person.name;
}
// prevProps.person === newProps.person will return false due to recreation of object

When to use memo?

  1. You must use React.memo with functional components.

  2. When a component gets re-rendered very often with the same props then it is best to use memo

For example, if you are creating an e-commerce site where you are displaying a countdown clock for an offer whose value gets updated every second and also has a product's details as a sibling component whose props are going to remain the same then you can wrap that product detail component in memo to avoid re-rendering of it.

Now you know the memoization concept then it is not a good practice to apply it to every component. So, do it wisely.

If the component is not rendering very often or rendering with different props most of the time then there is no need of using memo

Incorrectly applied enhancement can even damage the performance

Final words

React.memo is good for UI performance but use it wisely and correctly.

If you find something wrong in this blog then please do let me know in the comments.

Want to connect with me or know about me? Click here

Did you find this article valuable?

Support Yash Paneliya by becoming a sponsor. Any amount is appreciated!