12/02/2023
6 min read
React

Content:

Understanding useMemo and useCallback by understanding memoization first




Introdution


Libraries and frameworks often re-implement well known patterns for us so we don't have to.

But if we don't know those patterns the library or the framework can feel a bit magic for you, and because you don't know what is going on under the hood, you won't be able to use the framework correctly.

In this article we will explore the basics of useMemo and useCallback by understanding the pattern that is behind those two hooks who is named memoization.


Memoization


Memoization is a scary word that simply mean caching the result of an operation and if we perform the same operation again instead of performing this operation from scratch we just find and deliver the cached result of this operation.

for cahing the operation we usually use a HasMap in Java or a Map or an object in Javascript. The classical exemple to demonstrate memoization is the Fibonacci sequence. The Fibonacci sequence is a sequence of numbers in which each numbers is the sum of the two previous fibonnaci numbers (the sequence start with 0 for fibonnaci(0) and 1 for fibonnaci(1)), for exemple:


1 2 3 4 5 6 7 8 9 10 11 12 13 14 fibonnaci(0) //0 fibonnaci(1) //1 fibonnaci(2) //1 because 1 + 0 fibonnaci(3) //2 because 1 + 1 fibonnaci(4) //3 because 1 + 2 fibonnaci(5) //5 because 3 + 2


You can solve this problem in two different ways tabulation and recursion.


1 2 3 4 5 6 7 8 9 10 11 const fibTabulation = (num: number):number => { if (num === 0) return 0; let fibSuite = [0, 1]; for (let i = 2; i < num + 1; i++) { fibSuite = [...fibSuite, fibSuite[i - 1] + fibSuite[i - 2]]; } return fibSuite[fibSuite.length - 1]; }; const fibRecursion = (num:number):number ⇒ num < 2 ? num : fibRecursion(num - 1) + fibRecursion(num - 2);


The second option is shorter but is way less performant because there is a bunch of duplicate operations, as you can see in the image below for fibonnaci(6) .




Let's fix that by memoizing our recursive algorithm.


1 2 3 4 5 6 const memoizedFib = (num: number, memo: Record<number, number> = {}): number => { if (num in memo) return memo[num]; if (num <= 2) return num; memo[num] = fib(num - 1, memo) + fib(num - 2, memo); return memo[num]; };


Now our algorithm got rid of all of the redundant operations by caching the results in a simple object. You can see the new operations flow on the image below.




And that's it if you followed and understood what we have done until here, you know what is memoization and you'll quickly grasp how useMemo and useCallback

work.


useMemo


useMemo will let you cache the result of a calculation between re-renders so if you understood the exemple with the fibonnaci sequence you also understand useMemo.

Let's see a basic exemple.


1 2 3 4 5 6 7 8 9 10 11 12 import {useState,useMemo} from ‘react’; import {fibonnaci} from ‘../fibonnaci’; const SomeComponent = () ⇒ { const [count,setCount] = useState(0); const calculation = useMemo(()⇒ fibonnaci(5),[]); // will be excuted once even if the component re-render return <div> <div>{calculation}<div> <button onClick={()⇒ setCount(prevCount ⇒ prevCount + 1)}> <div> };


useMemo takes a function as first parametter and takes a dependency list as a second parametter. in this exemple useMemo does exactly what did

our memoizedFib function in our previous exemple. in this exemple useMemo as an empty dependency list so the calculation will happen only once no matter how

many time the component re-render. Let's see another exemple when useMemo has a dependency.


1 2 3 4 5 6 7 8 9 10 11 12 import {useState,useMemo} from ‘react’; import {fibonnaci} from ‘../fibonnaci’; const SomeComponent = () ⇒ { const [count,setCount] = useState(0); const calculation = useMemo(()⇒ fibonnaci(count),[count]); // will be excuted only if count is updated return <div> <div>{calculation}<div> <button onClick={()⇒ setCount(prevCount ⇒ prevCount + 1)}> <div> };


This time we calculated the fibonnaci number for the count state who can change so we pass it in the dependency array of useMemo who will re-do the calculation

only if the count value change.


Conclusion


I hope that now you know the basics of useMemo, how, when and why use it. But keep in mind that it was just an introduction if you want to know all about useMemo go read the React documentation there is some more advance use cases .

It is worth noting that you probably don't need useMemo if you don't have a performance problem.


useCallback


useCallback is very similar to useMemo but instead of caching the result of the function between re-renders it just cache the function itself between re-renders.

Just like useMemo it takes a function as a first parametter and a dependency array as a second parametter.


1 2 3 4 5 6 7 8 9 10 11 12 13 import {useState,useCallback,useMemo} from ‘react’; const SomeComponent = () ⇒ { const [count,setCount] = useState(0); const fibonnaci = useCallback(()⇒ (num:number):number ⇒ num < 2 ? num : fibRecursion(num - 1) + fibRecursion(num - 2),[]); // The function will be created only once const calculation = useMemo(()⇒ fibonnaci(count),[count]); return <div> <div>{calculation}<div> <button onClick={()⇒ setCount(prevCount ⇒ prevCount + 1)}> <div> };


In this exemple the fibonnaci function will be created only once and not re-created if the component re-render. If the function is not pure and has some dependencies

we need to pass the dependencies in the dependency list of useCallback and the function will be recreated when one of the dependency change.


Conclusion


Hopefully it was not too complicated to understand the basics of useCallback , maybe it was even fairly simple if you already understood memoization and useMemo.

You can visit the React documentation if you want to go beyond this introduction. Like useMemo you certainly don't need to optimize your code by using useCallback

if you don't have any noticable performance problem.


Wrap up





I hope that you found this article helpful , if you want to learn something about another react hook (useContext) you can click here.