In React, you can use useMemo and useCallback to optimize the performance of useless rendering. This article is my own study of useMemo and useCallback notes, introduced their use scenarios and differences.

1. useMemo

UseMemo is used to cache values, saving values that do not change with each rendering, reducing computation. This is especially useful in scenarios where overhead is high and values do not change.

As shown in the code below, the two states Max and count are defined, and the current values of Max and count are displayed on the page, and the values of Max and count can be increased by clicking the button.

A function getSum is defined to sum 0 to Max, simulating an expensive operation and displaying the sum on a page. But the operation of the sum depends only on the Max state, and has nothing to do with other values.

import { useState } from 'react';

const A = () = > {
    const [max, setMax] = useState(100);
    const [count, setCount] = useState(0);

    // Simulate an expensive operation
    const getSum = () = > {
        console.log('Sum is recalculated');
        let sum = 0;
        for (let i = 0; i <= max; i++) {
            sum += i;
        }
        return sum;
    };

    return (
        <>
            <div>Sum: {getSum ()}</div>
            <div>Max: {Max}<button onClick={()= > setMax(max => max + 1)}>max++</button>
            </div>
            <div>Count: {count}<button onClick={()= > setCount(count => count + 1)}>count++</button>
            </div>
        </>
    );
};

export default A;
Copy the code

If you change the value of count, the sum does not change, because the sum depends only on Max. Without useMemo optimization, as shown in the figure below, the getSum function is called again each time the value of count is changed, and each time an unnecessary operation is performed.

This can be optimized using useMemo, as shown in the following code. Wrap the function with useMemo and write the dependency Max in the second argument of useMemo to indicate that getSum will be called again only if Max changes, otherwise the last calculated value will be used.

import { useState, useMemo } from 'react';

const A = () = > {
    const [max, setMax] = useState(100);
    const [count, setCount] = useState(0);

    // Simulate an expensive operation
    const getSum = useMemo(() = > {
        console.log('Sum is recalculated');
        let sum = 0;
        for (let i = 0; i <= max; i++) {
            sum += i;
        }
        return sum;
        // Write dependencies that are recalculated only when Max changes
    }, [max]);

    return (
        <>{/* Notice the way this is written */}<div>Sum: {getSum}</div>
            <div>Max: {Max}<button onClick={()= > setMax(max => max + 1)}>max++</button>
            </div>
            <div>Count: {count}<button onClick={()= > setCount(count => count + 1)}>count++</button>
            </div>
        </>
    );
};

export default A;
Copy the code

As shown in the figure below, getSum is not called again when count is updated. GetSum is called again only when Max changes, reducing unnecessary function calls and rendering and achieving optimization.

Note:

  • useMemoIt returns a value, so it saysThe < div > sum: {getSum} < / div >Rather thanThe < div > sum: {getSum ()} < / div >You don’t have to call it yourself, so just feel the difference here.
  • The incominguseMemoThe function of phi is going to be inDuring the renderingDo not perform operations inside this function that are not related to rendering. And just for demonstration purposes, I’m doing it inside a functionconsole.log().
  • Don’t forget to write the correct dependency array. If no dependent array is provided,useMemoThe new value is computed on each render.

2. useCallback

UseCallback is used to cache functions. Usually used in parent-child components, the parent passes a function to the child, and when the parent updates, the function passed to the child is also recreated. Sometimes a function passed to a child component does not need to be recreated, so useCallback can cache the function without causing it to be recreated.

As shown in the code below, the parent component A creates two variables internally, num and count, which are displayed on the page and whose values can be updated. A function getCount is created that returns the value of count. Parent component A passes getCount to child component B.

Child component B gets the getCount function, calls it and displays the count value on the page. To demonstrate whether getCount is recreated with each update, the Set data structure is used. Inside the B component, store a reference to the getCount function into the Set and display the length of the Set. If the length increases, the getCount function has been recreated.

import { useState } from 'react';

const set = new Set(a);const A = () = > {
    const [num, setNum] = useState(0);
    const [count, setCount] = useState(0);

    const getCount = () = > count;

    return (
        <>
            <B getCount={getCount} />
            <div>Count: {count}<button onClick={()= > setCount(count => count + 1)}>count++</button>
            </div>
            <div>Num: {num}<button onClick={()= > setNum(num => num + 1)}>num++</button>
            </div>
        </>
    );
};

const B = ({ getCount }) = > {
    set.add(getCount);
    return (
        <>
            <div>Count: {getCount ()}</div>
            <div>Number of elements in set: {set.size}</div>
        </>
    );
};

export default A;
Copy the code

The result is shown in the figure below. When num changes, the parent component A is triggered to update and the getCount function passed to B is recreated. However, the getCount function returns count. If num changes, there is no need to recreate getCount again, resulting in a waste of performance.

You can use useCallback for performance optimization by wrapping the getCount function with useCallback and writing the dependency array [count] to indicate that the getCount function will be recreated only if the count changes.

import { useState, useCallback } from 'react';

const set = new Set(a);const A = () = > {
    const [num, setNum] = useState(0);
    const [count, setCount] = useState(0);

    const getCount = useCallback(() = > count, [count]);

    return (
        <>
            <B getCount={getCount} />
            <div>Count: {count}<button onClick={()= > setCount(count => count + 1)}>count++</button>
            </div>
            <div>Num: {num}<button onClick={()= > setNum(num => num + 1)}>num++</button>
            </div>
        </>
    );
};

const B = ({ getCount }) = > {
    set.add(getCount);
    return (
        <>
            <div>Count: {getCount ()}</div>
            <div>Number of elements in set: {set.size}</div>
        </>
    );
};

export default A;
Copy the code

The result is shown in the figure below. When count is updated, the getCount function passed to component B is recreated. When num is updated, the getCount function is not recreated, which reduces unnecessary function creation overhead and achieves optimization.

Note:

  • becauseuseCallbackIt returns onefunctionSo it’s still on the pageThe < div > count: {getCount ()} < / div >, you need to call it yourself. Feel the difference withuseMemoThe difference between.

The above is my humble opinion of learning, if there is anything wrong, welcome to point out the exchange!