React: What is useCallback Hook and When to Use It

One of the most important concepts to understand for optimizing React is memoization. Memoization is a performance optimization technique that eliminates the need to recompute a value for a given input by storing the original computation and returning that stored value when the same input is provided. Caching is a form of memoization.

React has three APIs for memoization: memo, useMemo, and useCallback. The caching strategy React has adopted has a size of 1. Meaning that they only keep around the most recent value of the input and result.

There are specific reasons both useMemo and useCallback are built-in to React:

  1. Referential equality
  2. Computational expensive calculation

Consider the Blurb component below:

function Foo({bar, baz}) {
  const options = {bar, baz}
  React.useEffect(() => {
    buzz(options)
  }, [options]) // we want this to re-run if bar or baz change
  return <div>foobar</div>
}

function Blub() {
  return <Foo bar="bar value" baz={3} />
}

This is problematic because useEffect is going to do a referential equality check on options between every render, and thanks to the way JavaScript works, options will be new every time so when React tests whether options changed between renders it'll always evaluate to true, meaning the useEffect callback will be called after every render rather than only when bar and baz change.

We can use useCallback to fix it, especially if bar or baz are non-primitive like objects/arrays/functios/etc:

function Foo({bar, baz}) {
  React.useEffect(() => {
    const options = {bar, baz}
    buzz(options)
  }, [bar, baz])
  return <div>foobar</div>
}

function Blub() {
  const bar = React.useCallback(() => {}, [])
  const baz = React.useMemo(() => [1, 2, 3], [])
  return <Foo bar={bar} baz={baz} />
}

The reason is because when you define an object inside your React function component, it is not going to be referentially equal to the last time that same object was defined (even if it has all the same properties with all the same values).

The Difference Between useMemo and useCallback

useMemo is to memoize a calculation result between a function's calls and between renders, Whereas useCallback is to memoize a callback itself (referential equality) between renders.

useMemo focuses on avoiding heavy calculation. useCallback focuses on a different thing: it fixes performance issues when inline event handlers like onClick={() => { doSomething(...); } cause PureComponent child re-rendering (because function expressions there are referentially different each time)

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.

This means useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).

Conclusion

useMemo and useCallback are performance optimizations. Use them only when you already have a performance problem instead of pre-emptively.

Also because some inline functions with useCallback are not always more performant. For example:

const dispense = candy => {
  setCandies(allCandies => allCandies.filter(c => c !== candy))
}

is more performant than

const dispense = candy => {
  setCandies(allCandies => allCandies.filter(c => c !== candy))
}
const dispenseCallback = React.useCallback(dispense, [])

They're exactly the same, except the useCallback version is doing more work. Not only do we have to define the function, but we also have to define an array ([]) and call the React.useCallback, which itself is setting properties/running through logical expressions etc.

React is VERY fast, and there are so many things you can do with with your time that would be better than optimizing stuff like this -- shipping your product, for example. Write your code so that it still works without memoization — and then add it to optimize performance once needed.

Be the first to know when I create something new.
Join my newsletter.