1

could someone explain to me if the use of useMemo and useCallback is expensive or cheap? I've checked the React's source code and I think they are cheap to use, but I've also read some people saying they are not.

In a Next.js context using useCallback:

const MyComp = () => {
  const router = useRouter():
  const handleClick = useCallback(() => router.push('/some-path'), [router]);
  return <button onClick={handleClick} />
}

vs plain arrow function here:

const MyComp = () => {
  const router = useRouter():
  return <button onClick={() => router.push('/some-path')} />
}

Am I saving re-renders with useCallback? The cost of memoize and comprare the dependencies array [router], is more expensive?

Additional info: Checking the React's code I saw that they compare the deps array items using Object.is instead of === or ==.

Someone knows why?

Also checking this component render:

import {useCallback, useState} from "react";

let i = 0

const CallbackDemo = () => {
    const [value, setValue] = useState('');
    console.log('render', i++);
    const handleClick = useCallback(() => setValue(Math.random()), []);
    return (
      <div>
        <span>{value}</span>
        <button onClick={handleClick}>New set</button>
      </div>
    );
}

export default CallbackDemo;

I saw the same count of renders using or not using useCallback

  • You can read the [docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#description) for difference between `Object.is` and `===`. And [this blog](https://kentcdodds.com/blog/usememo-and-usecallback) talks about when to use `useMemo` and `useCallback` and when not to. – Ajeet Shah Apr 28 '21 at 12:15

2 Answers2

2

Am I saving re-renders with useCallback?

Not in that specific case, no. The useCallback code does let you save roughly the cost of an assignment statement because it doesn't have to update the click handler on the button element, but at the cost of a function call and going through the array of dependencies looking for differences. So in that specific case, it's probably not worth doing.

If you had a more complex child component, and that component was optimized not to re-render when called with the same props (for instance, via React.memo or React.PureComponent or similar), or you were passing the function to a bunch of child components, then you might get some performance improvements by not making them re-render.

Consider this example, where simpleClickHandler is always recreated but memoizedClickHandler is reused¹ via useCallback:

const { useState, useCallback } = React;

const Parent = () => {
    const [counter, setCounter] = useState(0);
    console.log("Parent called");

    const simpleClickHandler = () => {
        console.log("Click occurred");
        setCounter(c => c + 1);
    };
    const memoizedClickHandler = useCallback(() => {
        console.log("Click occurred");
        setCounter(c => c + 1);
    }, []);
    
    return (
        <div>
            Count: {counter}
            <Child onClick={simpleClickHandler} handlerName="simpleClickHandler">simpleClickHandler</Child>
            <Child onClick={memoizedClickHandler} handlerName="memoizedClickHandler">memoizedClickHandler</Child>
        </div>
    );
};

const Child = React.memo(({handlerName, onClick, children}) => {
    console.log(`Child using ${handlerName} called`);
    return (
        <div onClick={onClick}>{children}</div>
    );
});

ReactDOM.render(<Parent/>, document.getElementById("root"));
<div id="root"><?div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

Note that when you click, only the child being passed simpleClickHandler re-renders, not the one being passed memoizedClickHandler, because memoizedClickHandler's value is stable but simpleClickHandler's value changes every time.

Using useCallback requires more work in the parent (checking to see if the previous function can be reused), but may help save work in child components.

It's important to note that the stability useCallback (and useMemo) give you are only appropriate for performance reasons, not correctness. React can throw away the previous copy of a handler and new a new one if it wants to, even if the deps haven't changed. If you need a correctness guarantee (the result definitely 100% will not change unless the deps change), you have use a ref. But that's a very rare use case.

Additional info: Checking the React's code I saw that they compare the deps array items using Object.is instead of === or ==.

Someone knows why?

Primarily because NaN === NaN is false, because all comparisons with NaN are false. So if they used ===, any deps array containing NaN would always be considered different from the previous one. But using Object.is avoids that problem, because Object.is(NaN, NaN) is true.


¹ Note that "reused" here means: a new handler is created every time, just like simpleClickHandler, but useCallback may return the previous handler you've given it if the deps haven't changed, so the new one is thrown away. (JavaScript engines are really quick at allocating and reclaiming short-lived objects.)

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
0

useCallback like useMemo indeed improve performance but!!! you don't need to use it to everything because it will make your website slower. this is better for the heavy lifting components like for charts and stuff like that. that consume a lot of resource and take a lot of time to process and this will make this specific component load much more faster and not stuck everytime you do a change.

you can see deep compression like this:

react useEffect comparing objects

this link will show you for instance how to do a deep compression for use effect

TalOrlanczyk
  • 1,205
  • 7
  • 21