6 MIN READ
   //   Mar 26, 2020

Optimize your React App Performance with Memoization

vrushali.bhosale

Memoization in React is a performance feature that aims to speed up the render process of components. We will be exploring this technique in React, with sample use cases along the way.

| What is Memoization?

Memoization is the process of caching a result of inputs linearly related to its output. So when the input is requested the output is returned from the cache without any computation. The multiple render() operations for generating the same resulting output for a similar set of inputs can be prevented. We can catch the initial render result and refer to that result in the memory next time. 

Memoization is an optimization technique used to primarily speed up programs by storing the results of expensive function calls and returning the cached results when the same inputs occur again.

Note: React.PureComponent performs optimization that uses componentShouldUpdate() lifecycle method that makes a shallow comparison of props and state from the previous render of the component. As shallow rendering only tests state and props of the component and does not test state and props of the child components with React.PureComponent.

React.PureComponent is only restricted for class components that rely on the shouldComponentUpdate() lifecycle method and state.

Memoising can be applied to both functional and class components. This feature implementation has both HOC’s and React hooks. React.memo() performs the same shallow comparison between props of the component and determines if the component should be rendered or not.

| Why should we use memo?

Let's take an example of search functionality. In the example below, the App component contains:

  1. Search input for the fruit name 

  2. A button and a child component where the user search will be displayed 

  3. A count of the number of times a user has clicked the button

export default function App() {
   const fruits = ["apple", "orange", "banana"];
   const [fruitName, setFruitName] = useState("");
   const [searchedFruit, setSearchedFruit] = useState(
     "Search your favorite fruit"
   );
   const [count,setCount] =useState(0);
   const searchFruitName = () => {
     if (fruits.includes(fruitName)) {
       setSearchedFruit(fruitName);
     } else {
       setSearchedFruit("No results Found");
     }
     setCount(count+1);
   };
    const showAllFruits = () => {
     return fruits.map((fruit, index) => {
       return (
         <span key={index} className="fruitname">
           {fruit}
         </span>
       );
     });
   };
   return (
     <div className="App">
       <h3>Count: {count}</h3>
       <div className="fruits">{showAllFruits()}</div>
       <div>
         <input
           type="text"
           placeholder="Search.."
           onChange={event => setFruitName(event.target.value)}
           value={fruitName}
         />
         <button onClick={searchFruitName}>Search</button>
       </div>
       <DisplayFruitName searchedFruitName={searchedFruit} />
     </div>
   );
 }

Here is the DsiplayFruitname component that will display the searched fruit name if it's available in the given fruits array and we are also consoling the name prop to check how many times the child component gets re-rendered.

Check out the running example here - https://codesandbox.io/s/modest-lake-oo1iy

const DsiplayFruitname = ({ searchedFruitName }) => {
   console.log("re-rendered :", searchedFruitName);
   return <h3>{searchedFruitName}</h3>;
 };
export default DsiplayFruitname;

In the above code, the DsiplayFruitname component will be called for each search value on every button click even if we have searched the same fruit name previously. That does not make any sense. Why would we call the component repeatedly when search prop remains the same and that renders the exact same output?

| Using React.memo

React.memo is a higher-order component that renders the component only if props are changed. Hence we wrap our component in a memo()

import { memo } from 'react';
const DsiplayFruitname = ({ searchedFruitName }) => {
 console.log("re-rendered :", searchedFruitName);
 return <h3>{searchedFruitName}</h3>;
};
export default memo(DsiplayFruitname);

Try editing the code with and without React.memo here - https://codesandbox.io/s/modest-lake-oo1iy 

If the same fruit name appears again as the previous one it will not re-render our DsiplayFruitname component as per the definition of Memoization. We can check this with the count shown on button clicks and how many times the child component actually gets re-rendered. As it memoises the result of the wrapped component and returns that result if the same search appears again.

React.memo also gives us an option to pass our own comparison function as a second argument.

This tells whether component rendering is needed or not.

function myComparison(prevProps, nextProps) {
...
}
export default React.memo(WrappedComponent, myComparison);

myComparison() returns true if passing nextProps to render would return the same result as passing prevProps to render, otherwise returns false.

| The useMemo hook

Memoization can also be done by using hooks and functional components. We can use inline JSX from the component return and can memoise results in variables as well. We can also memoise callback functions using useCallback hook API. You can further read more about useCallback hook here https://reactjs.org/docs/hooks-reference.html#usecallback

// useMemo signature
const result = useMemo(() => computeComponentOrValue(a, b), [a, b]);

Returning to our previous example, we can use useMemo hook for rendering the fruit name from the child component. We need to use the JSX returned from the child component and use that inside useMemo hook. The parent-call to child component can be changed to useMemo as below 

{useMemo(
   () => (
      <DisplayFruitName searchedFruitName={searchedFruit} />    
   ),
   [searchedFruit]
 )}

If you have noticed, with different fruit names searched for the child components, react memoization fails at one thing.

Consider if the searched fruit name is an apple, initially it will render the child component. Next button click searches apple again, this time it will not re-render. Yay!! If you search for an orange, the child component will get re-rendered. But later if the user searches for apple again then it will fail and the child component gets re-rendered which it shouldn’t because it has already seen the input before and should return the result as it computed earlier from the cache. This is where React memoization fails.

| When and how memoization can be used in our React apps?

  1. Try to divide a big component into one or more child components to leverage the memoization. For instance, consider a big dynamic component containing static components like buttons, labels, and static UI presentations that needs re-rendering. These can be separated and memorization is possible.

  2. It's better to remove memoization if you don’t see any performance gain after implementing it, this can free up some memory space too.

| Conclusion 

We have seen what is memoization and the power of memoization with an example of how it can speed up our React apps.
Memoization may seem to be useful but it also comes with a trade-off. It occupies the memory space for storing the memoised values. It may not be useful with low memory functions but delivers great benefits with high memory functions.