Hooks Revisited: useCallback

  • react
  • hooks

Before you proceed…

If you haven’t read my useMemo article yet, I highly suggest you go back and do so now! In that article, we covered important concepts like memoization, which we will continue discussing below.

Since useMemo and useCallback are similar (with one key difference), it will be important to understand how useMemo works before proceeding.

What’s the difference?

Both useMemo and useCallback utilize memoization to optimize performance, however, there is a subtle difference between them. While useMemo returns a memoized value resulting from the logic contained within the hook’s body, useCallback returns a memoized version of the function itself.

In the code block below, I’ve taken the useCallback example from the React docs and placed it next to its useMemo equivalent to better illustrate the difference:

// memoizedFunction is a function
const memoizedFunction = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

// memoizedFunction is the value returned from doSomething(a, b)
const memoizedValue = useMemo(() => {
  doSomething(a, b);
}, [a, b]);

Here, useMemo and useCallback achieve the same thing: optimizing performance by returning cached values when a function has already been executed using the arguments it receives. Since they return different values, both hooks offer you a different way to leverage memoization based on your specific use-case.

In practice

useCallback is useful because you can assign a memoized function to a variable and pass it around your application. This allows you to avoid re-creating the caching mechanism that memoization uses to improve performance.

It also makes our lives easier because we do not need to duplicate useMemo logic in multiple places. We also don’t need to import/export anything. Instead, we can just pass the memoized function as a prop and allow another component to consume it.

In the sandbox below, I’ve taken the code from our useMemo example and refactored it to use useCallback:

Like with useMemo, our useCallback hook is returning a memoized value. However, in this case, it is a memoized version of the anonymous function passed to it, not the function’s return value.

For demonstration purposes, we have two map components on this page (i.e. MapOne and MapTwo), which render — you guessed it — maps. If we assume they both plot coordinates in the same way, we can now pass createMapCoordinates to both components, allowing them to utilize the memoized function internally without having to recreate it in both places.

const myFunction = () => {
  doStuff();
};

If you think about it, what we’re doing here with useCallback isn’t too much different from the snippet above since both of them create a variable and assign a function as its value. Our hook just memoizes the function so we can optimize our applications performance!