skip to Main Content

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

How is it possible to cache function definitions? This example is taken from React docs:

import { useCallback } from 'react';

export default function ProductPage({ productId, referrer, theme }) {
  const handleSubmit = useCallback((orderDetails) => {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }, [productid, referrer]);

How is it possible to prevent the callback passed to useCAllback from being created? As far as I know, the JS runtime will create a new instance of the callback each time the ProductPage function is called (I have tested this behavior in JavaScript code).

What is useCallback optimizing? I also assumed that React calls the ProductPage function on every render.

I have searched Stackoverflow and google. I expect an explanation on why using useCallback is beneficiary.

2

Answers


  1. How is it possible to prevent the callback passed to useCallback from being created?

    It’s not possible, you’re right on that. But that’s not what useCallback is aiming at.

    The optimisation that useCallback enables is when using handleSubmit (i.e. the return value of useCallback) as a dependency of other hooks, e.g. useMemo or useEffect. But on its own, it’s pretty useless.

    Login or Signup to reply.
  2. Let’s re-order the questions a little:

    How is it possible to prevent the callback passed to useCallback from being created?

    It is not possible. The function ProductPage is called every time the component is instantiated, which creates a new instance of the (orderDetails) => { ... } callback.

    But – when we use useCallback, React uses the previous value unless any of the deps has changed, essentially throwing away the new instance.

    How is it possible to cache function definitions?

    It’s caching the value of the function itself. A standard dynamic cache would require an extra unique key to store/retrieve the cached value; since React’s rules of hooks ensure that each instance of a hook within a component instance has a fixed position, there is no explicit cache key required.

    What is useCallback optimizing?

    useCallback stabilises the callback value – essentially, the function doesn’t change unless it needs to change because one of the dependencies has changed.

    On its own, this doesn’t optimise anything – there is, of course, a cost associated with tracking the cached value and its dependencies. If, however, the callback is passed to other components or hooks, then it may cause re-rendering or other work. A self-contained if slightly contrived example:

    function useNotifier(instances, value) {
      // Notify all instances when value changes
    
      const notifyAll = useCallback((value) => {
        instances.forEach(instance => instance.notify(value));
      }, [instances]);
    
      useEffect(() => {
        notifyAll(value);
      }, [notifyAll, value]);
    }
    

    If we don’t stabilise notifyAll using useCallback, then the value of the callback changes every time the hook is rendered, even if the instances and value have stayed the same.

    The useCallback docs do go into better explanations of the situations in which stabilising a function using useCallback (and similarly useMemo) can reduce the amount of work done in re-renders, in particular a couple of deep dives towards the bottom.

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