React hooks, a new method to manage state in functional components, was introduced in React v16.8. With hooks like
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.
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,
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.
Lets look at some example code where we have a
factory function that’s returning a function:
Here, we create
function2 using the
factory), compared, and or anything a normal object can do.
You can see that although
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
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
functionsSet will increase by two, since we’re recreating both the
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.
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
- 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.