Search code examples
reactjsreact-hooksusecallback

React hooks: is `useCallback` not so needed usually?


I am recently refactoring a web app using React Hooks. I encounter a problem regarding useCallback. Based on description of Kent: https://kentcdodds.com/blog/usememo-and-usecallback, useCallback is to pass in identical function reference to sub-components, to avoid re-render of sub-components, so that the performance is better. However, it's used together with React.memo. And as Kent said:

MOST OF THE TIME YOU SHOULD NOT BOTHER OPTIMIZING UNNECESSARY RERENDERS. React is VERY fast and there are so many things I can think of for you to do with your time that would be better than optimizing things like this. In fact, the need to optimize stuff with what I'm about to show you is so rare that I've literally never needed to do it ...

So, my question is: am I right to claim that we do not need to use useCallback usually? except when the callback is expensive to create, using useCallback avoids re-creating the callback for every render.

Say, for a onClick or onChange event handler, 2 lines or less, shall we just not use useCallback to wrap it?


Solution

  • I find the useCallback() is necessary when I don't want the function reference to change. For example, when I'm using React.memo on some child component that should not be re-rendered as a result of a reference change in one of its methods that comes through props.

    Example:

    In the example below Child1 will always re-render if Parent re-renders, because parentMethod1 will get a new reference on every render. And Child2 will not re-render, because the parentMethod2 will preserve its reference across renders (you can pass a dependency array to make it change and be re-created when new input values come).

    Note: Assuming the Child components are being memoized with React.memo()

    function Parent() {
      const parentMethod1 = () => DO SOMETHING;
      const parentMethod2 = useCallback(() => DO SOMETHING,[]);
      return(
        <React.Fragment>
        <Child1
          propA=parentMethod1
        />
        <Child2
          propA=parentMethod2
        />
        </React.Fragment>
      );
    }
    

    On the other hand, if the function is expensive to run, you can memoize its results using the useMemo hook. Then you will only run it when new values come, otherwise it will give you a memoized result from previous calculations using those same values.

    https://reactjs.org/docs/hooks-reference.html#usecallback

    useCallback

    Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate).

    useMemo

    Pass a “create” function and an array of dependencies. useMemo will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.