useCallback and useMemo in Reactjs

Edward Luu
4 min readSep 18, 2021

The movement from class components approach to functional components approach has brought about many advantages for React developers. With the introduction of Hooks, we are now able to combine the same logic between renders, it makes the code look cleaner and improves legibility. Reactjs also provides 2 built-in hooks called useCallback and useMemo to help us optimize performance of our application.

I. What?

As the doc says:

“useCallback: returns a memoized callback.”
“useMemo: returns a memoized value.”

There might be confusions between these notions at the first glance due to the similar usage. Both accept a function & an array of dependencies. The difference is that useCallback returns its function while useMemo calls its function then returns the result whenever there are changes in the dependency array. To put it in a nutshell, useCallback and useMemo methods help us to avoid re-creating or re-running functions.

II. Why & How?

Functional components fundamentally are the same as the render function in class components. According to this, when a function is defined inside a functional component, it will be re-created and re-evaluated on every re-render. In addition, if a functional parent component has a function passed as a property to a child component, it will cause its child to re-render no matter what.

Take a look at this example and you will see everytime you click the CountButton, it causes a change in App’s state. Therefore, RandomButton gets re-rendered(different number). Why does this happen? Let’s take a deeper look.

You might have already known that function is an object in Javascript and react component will re-render if its props or state changes. If you define a function(object) inside a react functional component, it is not going to be the same function(object) you have defined before between re-renders, which results in referential inequality. Because of this reason, everytime when you change the App component’s state, the “random” function is re-created, and the property “random” of RandomButton will take a new value which leads to re-render itself.

To solve this problem and prevent possible performance issues, we can use useCallback & useMemo. Let’s see how we can take advantage of these two methods.

useMemo

“useMemo is to memoize a calculation result between a function’s calls and between renders.”

In the above example, we have a parent component App, two children components called CountButton and RandomButton. The expected behavior is that whenever we click the CountButton, it will not change the result of RandomButton before. How can we approach this? By memorizing the random function’s result. Let’s wrap it inside useMemo like this:

const random = useMemo(() => {
return Math.random()
},[]);

Don’t forget to change <RandomButton random={random()}/> to <RandomButton random={random}/>

useMemo receives two params:
+ A callback function
+ An array of dependencies: Conceptually, every value referenced inside the callback should also appear in the dependencies array.

useMemo prevents the callback function from being rerun if there are no changes in the dependency array. Use it when you want to avoid heavy calculations.

useCallback

“useCallback is to memorize a callback itself (referential equality) between renders”

This hook focuses on a different thing, it is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders.

For instance:

Everytime you click on the Button 1, it will trigger a change in state of the App component which leads to a re-render of the App. As a result, a new clickButton2 function(object) is re-created then passed to Button 2. Therefore, the dependency in useEffect of Button 2 is changed and causes a re-render of Button 2.

This is an unnecessary render here because what actually changes is the one we have clicked. To prevent our function from being recreated and changing reference on every render , wrap the clickButton2 in a useCallback like this below:
const clickButton2 = React.useCallback(() => {
console.log(‘clicked button 2’)
},[])

This hook will preserve and only re-created our function when there are any changes in the dependency array. Use it if you want referential equality between renders when you pass a function as a property to child component.

III. When?

Everything has two sides, so does performance optimization. What costs us here with useCallback is that our function is not removed in memory, use it when you want to avoid redundant renders of child components. With useMemo, it should be used when we need to cache expensive calculations whose dependencies change gradually.

References:

https://kentcdodds.com/blog/usememo-and-usecallback

https://stackoverflow.com/questions/54963248/whats-the-difference-between-usecallback-and-usememo-in-practice/54963730

https://medium.com/@jan.hesters/usecallback-vs-usememo-c23ad1dc60

https://levelup.gitconnected.com/understanding-the-difference-between-usememo-and-usecallback-ec956adb2004

https://blog.logrocket.com/react-usememo-vs-usecallback-a-pragmatic-guide/

--

--