How to Optimize Performance with useCallback

How to Optimize Performance with useCallback

You have just built a remarkable React application, but as it grows in complexity, it begins to slow down, the themes take a little longer to change, and your animations are slowing down. You set out to identify the root cause of these issues, and more often than not, the culprit is re-rendering.

This article will explain how to optimize your React application's performance using the useCallback hook.

Pre-requisites

This article assumes you have basic knowledge of Javascript ES6 and basic React concepts such as JSX, hooks, props and state.

Identifying Performance issues

I mentioned in the introduction section of this article that the most common cause of poor performance issues is re-rendering. How do you identify these issues? What are the most relatable scenarios in which these issues might arise? Have I even had these issues without realizing it?

Below are two scenarios that might have performance issues;

  • Scenario one: Almost everyone at every point in their career would build an e-commerce app. Suppose you have this e-commerce app where users can add multiple items to their cart. As the user removes from or adds to their cart, React might re-render all of the items in the cart, regardless of whether or not only an item was changed. This would cause the app to slow down, leading to poor user experience.

  • Scenario two: You happen to build an app that calculates factorials. I'm sure you are thinking, but this one is pretty straightforward. What you are not seeing here is that with large values, your app starts to act sluggish.

Understanding React's Rendering Process

It is essential to understand the concept of React rendering of user interfaces before delving into the explanation and usage of these hooks.

For every React application, there is a visual representation of the application's interface- the component tree. When there is any interaction in the application, changes propagate down this component tree and trigger the reconciliation process. Reconciliation identifies these changes and updates the virtual DOM before applying changes to the actual DOM. If there is a change in the parent component, due to the changes traversing down the component tree, both parent and children components get re-rendered.

useCallback

useCallback is a React hook that allows you to cache a function definition between re-renders, preventing unnecessary function recreations.

useCallback Syntax

const cachedFn = useCallback(fn, dependencies)

Syntax Definition

  • fn: This is the function you want to persist between re-renders

  • dependencies: This is an array of values referenced inside the fn code. If any of these values change, the function is recreated; otherwise, the cached function is returned.

How useCallback works

By default, React re-renders the entire component and functions defined within it when there is a change, both on the initial render and subsequent re-renders. useCallback allows you to store and return a stored function when the component re-renders.

Practical Implementation of useCallback: Skipping re-renders of components

I'll start by demonstrating the issues with re-rendering in an unoptimized code;

Excessive Re-renders

Consider a simple React application that increments the count and the age values on clicking their respective buttons. Here's a live code sandbox of the code;

All the components in this application are rendered initially. The problem arises when you interact with either of the two buttons. Every button click triggers a re-render of all components.

While this excessive re-rendering may not have a noticeable impact on performance in a simple app like this one, it can become a significant performance issue as the complexity of your application grows.

Optimizing with useCallback

Now, we will optimize the code with useCallback

If you click on either button and check the messages logged in the console, you will notice that only the function defined concerning the button clicked is re-rendered. Every other component remains unchanged and is not re-rendered. This was made possible with memo.

memo is a high-order component that skips the rendering of a component if its props have not changed since the previous render.

When not to use this hook

Does this mean you need to use useCallback in your code from now on? No, you do not. The React team recommends that if there is a problem in your code, performance-wise, you should try to identify the problem and then fix it. Do not use it if your app is rendering user interfaces only. Using the hook unnecessarily could lead to long, unreadable codebases.

References and further resources

Conclusion

Optimizing your React application is not just about speed, it is also about delivering exceptional user experiences. The useCallback hook is designed to optimize the rendering process and reduce unnecessary re-renders of components. This can result in a substantial performance boost, especially when dealing with sizable and intricate applications.

I am open to any feedback on how this article can be improved. If you have any React-related topics you'd want me to write on, please drop your suggestions and ideas in the comment section. Thank you.