How to Use useCallback to Write Better React Code
By: Caelin Sutch
Tags: JavaScript, Reactjs, Web Development, Software Development, React

React hooks, a new method to manage state in functional components, was introduced in React v16.8. With hooks like useState , useEffect , and others, developers could finally work with side effects in functional components.

In this article, we’ll be taking a close look at useCallback , one of the introduced React hooks, and how we can use it to write better React code.

Photo by Ferenc Almasi on Unsplash

Introduction to useCallback

useCallback is used to optimize the rendering behavior of React functional components. It’s useful when a component is being constantly re-rendered and there’s complex function behavior inside of it. We’ll be taking a look at a simple example of how to implement this hook to see how it can help us gain efficiency in re-rendering components.

Keep in mind that React is very fast already, performance optimizations should be used only if a React component begins to feel slow, we’ll take a look at when to use useCallback at the end of this article

Lets take a look at a very simple Counter React component:

This is pretty simple, we have two state variables that are holding numbers and three functions to change our state. However, the problem here is that every time the Counter component is re-rendered, all three functions, increment , decrement , and incrementOtherCounter are all recreated!

We can see that by using a Set and adding the function to the Set each time the function runs. Why Set? It only stores unique elements, or in our case, uniquely instantiated functions.

Now, every time we click a button we should see the functionsSet being logged, and increasing by three each time! This shows us that every time the component is re-rendered, entirely new instances of functions are created, an necessary operation.

At its core, this problem is due to the how Javascript determines function equality.

Function Equality in Javascript

Lets look at some example code where we have a factory function that’s returning a function:

Here, we create function1 and function2 using the factory . Functions in Javascript are first-class citizens, meaning that they’re represented as regular objects. The function object can be returned by other functions (like factory ), compared, and or anything a normal object can do.

You can see that although function1 and function2 both come from the same place and do the same thing, they’re inherently different objects, so they aren’t equal. Any Javascript function is only equal to itself.

Going back to React, when a component re-renders, every function inside of the component is recreated. useCallback makes it so we can memoize (or cache) the function instance between renders. This means that instead of recreating the function object, we can use the same function object between renders.

Lets update our counter increment/decrement functions to use useCallback

Notice how we use a dependency array as one of the function parameters of useCallback . As long as the values in the dependency array are the same between renders, React will continue using the memoized (or cached) version of the function. If the values in the dependency array change between renders, React will recreate the function.

In this case, our logs will now reflect this. When we click the button to run incrementOtherCounter , our functionsSet will only increase by one, since only count2 is being updated, so only one function is being recreated ( incrementOtherCounter ). We know this because count2 is only in the dependency array of incrementOtherCounter and not either of the other functions.

When we click either the + or - buttons, functionsSet will increase by two, since we’re recreating both the increment and decrement functions.

Although this is a very simple example, we can see how we can use useCallback to optimize components that have complex or resource intensive functions.

When not to use useCallback

However, let’s ensure we don’t go too overboard. useCallback has its own downsides, primarily code complexity. There are a lot of cases in which it doesn’t make sense to add useCallback and just accept function recreation. useCallback has its own performance drawbacks, as it still has to run on every component re-render.

In this example, useCallback is actually not helping optimization, since we’re creating handleClick on every render anyways:

In this case, the optimization costs more than not having the optimization.

When thinking about performance upgrades like useCallback , always profile (or measure) your component speed before beginning the optimization process. Optimization adds complexity, and as a developer you always want to make sure that this tradeoff is worth it.

Conclusion

useCallback is a very powerful React hook to optimize complex React components by memoizing functions to prevent recreation upon every render.

Before working with useCallback , make sure you analyze the following:

Does the speed increase actually exist with useCallback ?

? Does the speed increase warrant increased component complexity?

For more information on useCallback , I recommend looking at the React documentation. How are you using useCallback ? I’d love to hear about it in the comments!

Keep in Touch

There’s a lot of content out there and I appreciate you reading mine. I’m a undergraduate student at UC Berkeley in the MET program and a young entrepreneur. I write about software development, startups, and failure (something I’m quite adept at). You can signup for my newsletter here or check out what I’m working on at my website.

Feel free to reach out and connect with me on Linkedin or Twitter, I love hearing from people who read my articles :)