skip to Main Content

So supposedly, useCallback can cache a function, and I think the intention is to make the code run faster.

So for example, if I have:

  const handleChange = (ev) => {
    setMsg(ev.target.value);
  };

I also can change it to:

  const handleChange = useCallback((ev) => {
    setMsg(ev.target.value);
  }, []);

so that now the function cached. However, doesn’t this function need to be created every time the component is re-rendered anyway?

To test it, I change it to an IIFE, so that the function is spit out from it, and it would print out the function is being spit out.

See:
https://codesandbox.io/s/jolly-nightingale-zxqp8k

So every time when you type something into the input field, a new function is spit out, as seen in the console. So that means, the IIFE is executed every time any way, and that means, even when it is not an IIFE, the function literal is made into a function object each time. So how does that help the code run faster at all?

2

Answers


  1. However, doesn’t this function need to be created every time the component is re-rendered anyway?

    Yes, that’s correct. A new function will be created on every render, and then the new one will be thrown out in favor of the cached one.

    The speed up doesn’t come from skipping creating the function. Instead, it comes from other pieces of code being able to skip their work. And this happens because if they’re passed the same function every time they can know that nothing relevant has changed.

    For example, if you need to pass handleChange into the dependency array of a useEffect, it’s important to pass a stable reference each time or the effect will be re-run on every render:

    useEffect(() => {
      // ... do something with handleChange
    }, [handleChange]);
    

    Or if handleChange is being passed as a prop to a component, and that component wants to skip rendering with React.memo. The render can only be skipped if the props don’t change:

    const Parent = () => {
      const handleChange = useCallback((ev) => {
        setMsg(ev.target.value);
      }, []);
      return <Child handleChange={handleChange}/>
    }
    
    const Child = React.memo(({ handleChange }) => {
      // ... do something with handleChange
    })
    
    Login or Signup to reply.
  2. The question has many answers. Because the answer changes it’s logic based on situation.

    The gist of the answer is "You have to understand the need and use accordingly"

    Suppose you have a function that you used as a props for other component. In this situation you can wrap the function in a useCallback. Sometime the component can be very simple but the dependency array of the useCallback hook can be a large and nested object. In that case wrapping the function in a useCallback hook might not be a good idea. Because the computation useCallback will do in every render doesn’t worth for the component to re-render.

    We have to keep in mind that React is designed to re-render the component. So when you ask React not to re-render that decision need to be a well thought decision.

    I got to know about this concept very well from the blog that is written by the creator of React testing library Kent C. Dodds. I liked how he explained about both useCallback and useMemo. Here is the link – https://kentcdodds.com/blog/usememo-and-usecallback

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search