Understanding useMemo

React ensures your user interface stays in sync with the application state by using a technique called re-rendering. During re-renders, React compares the Virtual DOMs and updates the necessary parts of the UI. This process is known as React Reconciliation.

Although React optimizes re-rendering for speed, some tasks can slow it down, leading to performance issues. To address this, React introduced the useMemo hook.

What is useMemo?

useMemo is a React Hook that caches the result of a calculation between re-renders.

Syntax:

const cachedValue = useMemo(calculateValue, dependencies);

useMemo memorizes values between re-renders, recalculating only when dependencies change. This avoids expensive calculations on every render. To the useMemo you can pass a function and an array of dependencies and it will only recompute the memoized value when one of the dependencies has changed. This avoids expensive calculations on every render and can improve performances.

Parameters

  • calculateValue: A pure function that takes no arguments and returns a value to be cached. React calls this function during the initial render and will return the same result on re-renders unless the dependencies change. If dependencies change, React will call calculateValue again and return its result.
  • dependencies: An array of reactive values referenced in calculateValue. These include props, state, and variables or functions declared directly in the component body. React recalculates the value if any dependency changes.

Remember, don’t do anything there that you wouldn’t normally do while rendering. For example, side effects belong in useEffect, not useMemo.

Usage

Below are the use cases of useMemo:

  • Skipping expensive calculations.
  • Skipping re-rendering of components.
  • Memozing a dependency of another Hook and function.

To understand the concept better, let’s dive into the example of skipping the expensive calculation using useMemo. In the given code, a slow function named slowFunctionNumber is executed every time the component initializes or the input changes. However, it also runs whenever the theme button is clicked, despite the theme button having no dependency on the number function. This unnecessary execution can reduce the application’s performance.

import {useState} from 'react';

// Simulating a slow calculation
const slowFunctionNumber = (num) =>{
    for(var i=1; i<10000; i++){
        console.log("slowFunctionNumber called.");
    }
    return  num * 2
}

const NonMemoPage = () => {

    const [number, setNumber] = useState(0);
    const [dark, setDarkTheme] = useState(false);

    const onNumberChangeHandler = (e) => {
        setNumber(e.target.value);
    };

    const onThemeChangeHandler = () => {
        setDarkTheme(prevDark => !prevDark);
    }
    // this function will makes the code slower.
    const doubleNumber = slowFunctionNumber(number);
    const styleClass = {
        backgroundColor: dark ? 'white' : 'black',
        color: dark ? 'black' : 'white',
        minHeight: '100px'
    }

    return (
        <>
        <input type="number" onChange={onNumberChangeHandler} value={number}></input>
        <br/>
        <button onClick={onThemeChangeHandler}>Change theme</button>
        <div style={styleClass}>{doubleNumber}</div>
        </>
    )
}

export default NonMemoPage;
}

In the above example, slowFunctionNumber artificially slows down the process by running a loop. In real scenarios, complex calculations might be involved, leading to significant performance issues.

To prevent slowFunctionNumber from running unnecessarily, you can use the useMemo hook, as demonstrated below. With useMemo, you can see that slowFunctionNumber is not executed when the theme change button is clicked. This happens because React memoizes the result and avoids re-rendering when there are no changes in the dependencies.

import {useMemo, useState} from 'react';

// Simulating a slow calculation
const slowFunctionNumber = (num) =>{
    for(var i=1; i<1000; i++){
        console.log("slowFunctionNumber called.");
    }
    return  num * 2
};

const MemoPage = () => {

    const [number, setNumber] = useState(0);
    const [dark, setDarkTheme] = useState(false);

    const onNumberChangeHandler = (e) => {
        setNumber(e.target.value);
    };
    const onThemeChangeHandler = () => {
        setDarkTheme(prevDark => !prevDark);
    }
    // useMemo will remember the function value if the dependencies is not changed.
    const doubleNumber = useMemo( () => {
        return slowFunctionNumber(number)
    }, [number]);
    
    const styleClass = {
        backgroundColor: dark ? 'white' : 'black',
        color: dark ? 'black' : 'white',
        minHeight: '100px'
    }

    return (
        <>
        <input type="number" onChange={onNumberChangeHandler} value={number}></input>
        <br/>
        <button onClick={onThemeChangeHandler}>Change theme</button>
        <div style={styleClass}>{doubleNumber}</div>
        </>
    )
}

export default MemoPage;

In this above code:

  • useMemo is used to memoize the result of slowFunctionNumber, ensuring it only re-runs when number changes.
  • The theme change button now only affects the theme without causing the slow function to re-run, improving overall performance.

Summary

useMemo is a React hook thats helps optimize performance by memozing the result of functions and ensuring its only re-renders when one of the dependencies are changed.

References

useMemo, Web Dev Simplified, Josh W Comeau, freecodecamp

Read More