Skip to main content
Bojan Gvozderac
Download CV

Please note that my resume in its current form has been edited and stripped of a lot of experiences and projects to be easier to read and communicate better what experiences I find most valuable / relevant.

If you'd like to get to know me better and my career journey feel free to reach out on the contact page.

useCallback

useCallback

I read the docs so you don't have to!

Today lets take a look at some highlights from the React docs about useCallback, you probably already know roughly what it does but lets take a look at the official explanation one more time:

“useCallback is a React Hook that lets you cache a function definition between re-renders.”

Basically you provide useCallback with a function you want memoized and an array of dependencies and if the dependencies don’t change React will return a cached version of that function instead of creating a new one, like so (example from docs):

const handleSubmit = useCallback((orderDetails) => {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }, [productId, referrer]);

But you probably already know this so here are some things from the docs you might’ve missed!

useMemo vs useCallback

Interviewing for a React dev position? You’ll get this question and here’s the answer in a nutshell:

  • useMemo caches the result of calling your function
  • useCallback caches the function itself

If you already know useMemo but not useCallback here’s how to think about it

function useCallback(fn, dependencies) {
  return useMemo(() => fn, dependencies);
}

useCallback everywhere?

What type of app should consider using useCallback

“If your app is like this site, and most interactions are coarse (like replacing a page or an entire section), memoization is usually unnecessary. On the other hand, if your app is more like a drawing editor, and most interactions are granular (like moving shapes), then you might find memoization very helpful.”

This! In A LOT of projects the type of project is never considered, it’s just assumed that useCallback will somehow magically make your app better no matter the situation.

When does it make sense to use useCallback

Caching a function with useCallback is only valuable in a few cases:

  • You pass it as a prop to a component wrapped in memo. You want to skip re-rendering if the value hasn’t changed. Memoization lets your component re-render only if dependencies changed.

  • The function you’re passing is later used as a dependency of some Hook. For example, another function wrapped in useCallback depends on it, or you depend on this function from useEffect.

There is no benefit to wrapping a function in useCallback in other cases. There is no significant harm to doing that either, so some teams choose to not think about individual cases, and memoize as much as possible. The downside is that code becomes less readable. Also, not all memoization is effective: a single value that’s “always new” is enough to break memoization for an entire component.

I’ve seen cases where a team adopts a “just wrap everything with useCallback it can’t hurt, right?” while the docs actually say there’s no significant harm in doing that I can’t shake the feeling it’s coming from this line of thinking “I don’t know exactly how useCallback optimizes functions but it can only help so just use it everywhere” which kinda sounds like “I don’t know and I don’t care” and I really can’t encourage that kind of thinking 😬

You might not need useCallback

In practice, you can make a lot of memoization unnecessary by following a few principles:

  • When a component visually wraps other components, let it accept JSX as children. Then, if the wrapper component updates its own state, React knows that its children don’t need to re-render.
  • Prefer local state and don’t lift state up any further than necessary. Don’t keep transient state like forms and whether an item is hovered at the top of your tree or in a global state library.
  • Keep your rendering logic pure. If re-rendering a component causes a problem or produces some noticeable visual artifact, it’s a bug in your component! Fix the bug instead of adding memoization.
  • Avoid unnecessary Effects that update state. Most performance problems in React apps are caused by chains of updates originating from Effects that cause your components to render over and over.
  • Try to remove unnecessary dependencies from your Effects. For example, instead of memoization, it’s often simpler to move some object or a function inside an Effect or outside the component.

Authoring a custom hook?

If you’re writing a custom Hook, it’s recommended to wrap any functions that it returns into useCallback
This ensures that the consumers of your Hook can optimize their own code when needed.

You can check out the full entry of the official useCallback docs here https://react.dev/reference/react/useCallback

I'm an experienced web and mobile developer specializing in JavaScript with over a decade and a half developing software I have proven that I have the knowledge and the expertise to deliver the goods!
If you'd like to get in touch and talk about potential projects, one of my articles, ask a question or just say "Hi!" please do by emailing bojan.gvozderac@monarch-software.com.