Performance optimization with useMemo and useReducer

React Hooks introduced some memory optimization hooks to improve performance.

Data Hooks are hooks which store data. You store data that the specified portion of the UI specifically relies on for visual changes and memoize/cache data that a given portion UI don’t directly rely on for visual changes. 

Storing is different from memoizing/caching.

There’s a blurry line though. useRef is a hook that can play both roles depending on how it’s used.

useState and useRef are data hooks. useRef, useCallback and useMemo are memoize hooks.

Let’s understand with an example.

import React, {useState} from 'react';

export default function Example() {

  const [count1, setCount1] = useState(0);

  const [count2, setCount2] = useState(0);

  const increaseCounter1 = () => {

    setCount((count1) => count1 + 1);

  };

  return (

    <div>

      <button onClick={increaseCounter1}>Increase counter</button>

      <Counter value={count1}>Counter1</Counter>

      <Counter value={count2}>Counter2</Counter>

    </div>

  );

}

const Counter = (value, children) => {

  return (

    <div>

      {children}:{value}

    </div>)};

In the above example, the counter component is rendered twice when we click the Increase counter button. We even didn’t update the second counter.

This can be overcome by using react memo. React overcomes this by comparing props passed to components wrapped in memo hook. It is similar to the react.pure component.

There is one important concept we should be aware of before understanding the useMemo and useCallback hook. i.e Referential equality.

Referential equality:

when you define an object inside your React function component, It will not be referentially equal to the last time the same object has been identified (even if it has all the same properties with all the same values). If a variable is a primitive (i.e., string, number, boolean, null, undefined, or symbol), then the reference never changes.

UseMemo Hook;

The use memo hook returns the memoized value passed in the callback as the first argument and the second argument is the dependency array. The dependent array determines the value on which the cashing of the function is done. Whenever the value of the dependency array changes the callback function is recalculated and again cashed. We use UseMemo where there is a very big function definition in the component to optimize the performance we have to cash the function.

Let’s take a look at the example.                     

const next = wordIndex + 1 === words.length ? 0 : wordIndex + 1;  

                        setWordIndex(next);  

                    }}  import React, { useState } from 'react'   

function RandomWords() {  

    const [count, setCount] = useState(0)  

    const [wordIndex, setWordIndex] = useState(0)    

    const words = ['This', 'is', 'React', 'Application', 'for', 'Testing', 'By', 'Priyanka']  

    const word = words[wordIndex]    

    const computeLetterCount = (word) => {  

        let i = 0;  

        while (i < 200000000) i++  

        console.log(i)  

        return word.length;  

    };    

    const letterCount = (word) => { computeLetterCount(word) };  

    return (  

        <div>  

            <div style={{ padding: '15px' }}>  

                <h2>Compute number of letters (slow)</h2>  

                <p>"{word}" has {letterCount(word)} letters</p>  

                <button  

                    onClick={() => {  

                >  

                    Next word  

                </button>   

                <h2>Increment a counter (fast)</h2>  

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

                <button onClick={() => setCount(count + 1)}>Increment</button>  

            </div>  

        </div>  

    )  

}    

export default RandomWords

Now in the above example, the computeLetterCount is computed on every render. Hence increasing the Computation and time to render. We pass the computeLetterCount function in the useMemo hook and improve the performance.

const letterCount = useMemo(() => computeLetterCount(word), [word]);

Now the computeLetterCount function will not compute every time on render and will only compute when the word is changed. Hence improving the performance.

UseCallback Hook;

useCallback is very similar to the useMemo hook the only difference is that the UseMemo hook calls the function as soon as the dependency array variable changes.

But useCallback hook returns the instance of the function passed instead of calling it.

useCallback – Allows you to cache an instance of a function between renders.

useMemo – Allows you to cache a value between renders.

When to use them?

If you are thinking of adding useCallback and useMemo hooks everywhere in your component, please don’t.

Adding performance optimizations using useCallback and useMemo is expensive and these optimizations don’t always bring enough benefits to offset their cost.

Both of these hooks add some extra complexity to your code and they require a lot of things working under the hood.

You should consider using useCallback and/or useMemo hooks on the following situations:

1) Processing large amounts of data

2) Working with interactive graphs and charts

3) Implementing animations

4) Incorporating component lazy loading (useMemo specifically)

 

Leave a Reply