React: Improve Performance With Memo()
By: Harsh Patel
Tags: JavaScript, Reactjs, Frontend Development, Programming, Coding

In this article, I provide some tips that can optimize or I can say improve the performance of your react app. Many times we got so many components on the same screen and eventually, that will take a lot of time to render.

Mostly I use pagination or virtualization for most apps. Pagination provides a good user experience and that’s the most needed thing while developing an app, but what if you have a case where you need to render many components on the same screen plus not giving up on the UX and app’s performance. Check this preview of the app. It has an App component and a Square component.

// App.jsx

import React, { useState } from "react";



import Card from "./components/square/square";



const data = Array(30000)

.fill()

.map((val, index) => {

return { id: index, key: `square-${index}` };

});



const App = () => {

const [count, setCount] = useState(0);



return (

<div>

<p>Count: {count}</p>

{data.map(({ key }) => (

<Card

key={key}

onClick={() => {

setCount(val => val + 1);

}}

/>

))}

</div>

);

};



export default App; // Square.jsx

import React from "react";



import "./square.css";



const Square = ({ onClick }) => {

return <div className="square" onClick={onClick} />;

};



export default Square;

If I add console in both components and then click on one of the square and check which called when and how many times. As an end result, I see the Square component is called 20k times. In addition, I can see that 600ms are wasted in re-rendering the UI on React Dev tools Profiler Tab. To check Start the profiling while the page load -> click square -> stop profiling.

SO, now you know that you need to stop re-rendering of Square component Because no change in props . For that, you need to use React.memo

What is React.memo ?

React.memo is also a component but it’s a higher-order component that helps to stop the re-rendering of a component by memoizing the result of the first render. It re-renders component only when the prop values are changed.

ADDITIONAL NOTE: React.memo does a shallow comparison. React.memo(Component, (prevProps, nextProps) => { // return true if the props are same, this will skip re-render // return false if the props have changed, will re-render });

Redefine Square component with React.memo

// Square component with React.memo

import React from "react"; import "./square.css"; const Square = ({ onClick }) => {

return <div className="square" onClick={onClick} />;

}; export default React.memo(Square);

Now repeat the same steps as you did previously.

You didn’t saw any difference yet. But whenever you hover on Square it shows onClick the prop has changed. As you know when something changes in props it re-render. This happens as in the code I passed function during each render for the onClick prop. To avoid this I need to use useCallback .

What is useCallback ?

useCallback is a react hook. returns value of it is a memoized callback.

// App component with useCallback

const App = () => {

const [count, setCount] = useState(0); const onClick = useCallback(() => {

setCount(val => val + 1);

}, []); return (

<div>

<p>Count: {count}</p>

{data.map(({ key }) => (

<Card key={key} onClick={onClick} />

))}

</div>

);

}; export default App;

Let’s do it again.

I still see a lag when I click on the square. To avoiding re-rendering of Square using memorization we did changes but React still need to compare the props of a square for all the 10k elements. Checkout this comparison tree,

Now I have 10k Square nodes below the main App component. To reduce the time I need to reduce instances of square components at the given level. Now certain questions popped up in the mind like What should be done? Can we add another layer of components just below it? Yes, that’s the solution, It needs to be split the list of 10k items into smaller chunks/components.

// App component

import React, { useState, useCallback } from "react";



import Row from "./components/row/row";



const data = Array(30000)

.fill()

.map((val, index) => {

return { id: index, key: `square-${index}` };

});



const chunkArray = (array, chunkSize) => {

const results = [];

let index = 1;



while (array.length) {

results.push({

items: array.splice(0, chunkSize),

key: String(index)

});

index++;

}



return results;

};



const allItems = chunkArray(data, 500);



const App = () => {

const [count, setCount] = useState(0);



const onClick = useCallback(() => {

setCount(val => val + 1);

}, []);



return (

<div>

<p>Count: {count}</p>

{allItems.map(({ items, key }) => (

<Row items={items} onClick={onClick} key={key} />

))}

</div>

);

};



export default App; // Row component

import React from "react";



import Square from "../square/square";



const Row = ({ items, onClick }) => {

return (

<>

{items.map(({ id, key }) => (

<Square key={key} onClick={onClick} id={id} />

))}

</>

);

};



export default React.memo(Row);

Let's see it again. No lags as of now. It has a lot fewer Row in addition, React can skip Square prop comparison if the Row props have not changed.

React.memo and useCallback can be used to get fast and good performance. It does not mean that you should wrap all components with React.memo and all functions with useCallback ? React.memo and useCallback use memoization which adds up to the memory, also the functions themselves take time to run and have overheads like the prop comparison. The splitting that we have seen adds up to the memory.

When to use React.memo and useCallback ?

They are not required unless you see some lag in a specific component or the complete app. If there is a lag try profiling for the actions on that screen and check if there can be any component re-renders that can be avoided.

Conclusion

While React.memo , useCallback , useMemo can be used to optimize the performance of the React apps they are not required in most cases. Use them Cautiously.

If you love this article help me to buy a coffee: https://www.buymeacoffee.com/harry1408

Harsh Patel | Full-Stack Developer | http://imharshpatel.com