←  TechnologyThu 21 Dec, 2023

useMemo vs. useCallback

What is the difference between useCallback() and useMemo(), Ngoc? I've been asked this question many times by my colleagues, so I decided to write this post, haha.


According to the official React documentation at react.dev:

useMemo is a React Hook that lets you cache the result of a calculation between re-renders.

const cachedValue = useMemo(calculateValue, dependencies)

useCallback is a React Hook that lets you cache a function definition between re-renders.

const cachedFn = useCallback(fn, dependencies)

I implemented the following small demo code to illustrate these definitions. You can see that no matter how many times I update the number value, onClickCallback retains the definition at the time it is defined, and it is updated only when the dependency changes. Similarly, like useMemo, memoNum only receives the new value when the dependency is updated, regardless of how many times 'num' is updated.
(Open the console to view logs.)


function App() {
  const [dependency, setDependency] = useState(0);
  const [num, setNum] = useState(0);

    const onClickCallback = useCallback(() => {
        console.log("--- useCallback - number ---", num);
    }, [dependency])

    const memoNum = useMemo(() => {
        return num;
    }, [dependency])

  return (
    <div className="App">
        <div>
            <button onClick={() => setDependency(dependency + 1)}>
                Dependency +
            </button>
            &nbsp;
            <span>{dependency}</span>
        </div>
        <div>
            <button onClick={() => setNum(num + 1)}>
                Number +
            </button>
            &nbsp;
            <span>{num}</span>
        </div>
        <Child onClickCallback={onClickCallback} memoNum={memoNum} />
    </div>
  );
}

const Child = ({onClickCallback, memoNum}: { onClickCallback: () => void, memoNum: number }) => {
    return <div>
        <br/>
        <span>Cache function definition with useCallback</span>&nbsp;
        <button onClick={onClickCallback} className={`demo-post-button`}>handleCallBack</button>
        <br/>
        <span>Cache the result of calculation, memoNum: {memoNum}</span>
        <br/>
    </div>
}
 0
 0

Cache function definition with useCallback 
Cache the result of calculation, memoNum: 0

Can you use useMemo to memoize a function, and vice versa, can you use useCallback to memoize a value?

Let's try to figure it out.

As you can observe from the code block below, callbackNum retains the definition of a function with a return type of a function that returns a number, rather than directly being a number. While it's possible to work around this by preserving the numeric value, doing so tends to make our code more verbose. It's important to note that useCallback is designed to specifically memorize function references, not their return values. This behavior is analogous to useMemo, where returning the entire function is required for a similar workaround.

(Open the console to view logs.)
function SwapTwoHooks() {
    const [dependency, setDependency] = useState(0);
    const [num, setNum] = useState(0);

    const callbackNum = useCallback(() => {
        return num;
    }, [dependency])

    const memoFunc = useMemo(() => {
        return () => {
            console.log("--- useMemo - number ---", num);
        };
    }, [dependency])

    const handleReset = () => {
        setDependency(0)
        setNum(0)
    }

    let rotate = 0;

    return (
        <div className="demo-post">
            <div>
                <button onClick={() => setDependency(dependency + 1)} className={`demo-post-button`}>
                    Dependency +
                </button>
                &nbsp;
                <span>{dependency}</span>
            </div>
            <div>
                <button onClick={() => setNum(num + 1)} className={`demo-post-button`}>
                    Number +
                </button>
                &nbsp;
                <span>{num}</span>
            </div>
            <Child callbackNum={callbackNum()} memoFunc={memoFunc}/>
        </div>
    );
}

const Child = ({callbackNum, memoFunc}: { callbackNum: number, memoFunc: () => void }) => {
    return <div>
        <br/>
        <span>Cache number value with useCallback</span>&nbsp;
        {callbackNum}
        <br/>
        <span>Cache function definition with useMemo, memoFunc:</span>
        <button onClick={memoFunc} className={`demo-post-button`}>memoFunc</button>
        <br/>
    </div>
}
 0
 0

Cache number value with useCallback 0
Cache function definition with useMemo, memoFunc:

So, here is my summary:

• You cannot directly use useMemo to memoize a function, nor useCallback to memoize a value.

• You should use the appropriate hook for its intended purpose to ensure: clarity, optimization and readability.

Merry Christmas, techies. 🎄🎄🎄