2.2.6: React useMemo - useCallback

Introduction

Weโ€™ve talked about useState and useEffect which is the building block of all React application. You can arguably create any React project with just these two hooks.

As your app scales, you might find that these hooks alone might not be enough to create an optimised application because thereโ€™ll be lots of state changes and side-effect that will be happening and it might slow your app down.

This is why React comes with some built-in hooks to help alleviate this

Hereโ€™s a list of additional React hooks from their documentation.

Before we dive in, letโ€™s take a look at one of the technique React uses called memoization

Memoization

So what is memoization?

From wikipedia -

โ€ฆ memoization is an optimisation technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again

The key takeaways are:

  • Optimisation technique

  • Speed things up

  • Returns cache result if input is the same

Letโ€™s take a look at an example of an expensive function call by implementing the Fibonacci number of N sequence

function fibonacci(n) {
	  if (n < 2) return 1;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

// What is the "4th" sequence in the Fibonacci's number?
fibonnacci(4)
// Answer: 3

// What is the "20th" sequence in the Fibonacci's number?
fibonnacci(20)
// Answer: 10946

// What is the "50th" sequence in the Fibonacci's number?
fibonnacci(50)
// Answer: uh oh something broke

As you can see, if you were to find the โ€œ50thโ€ sequence in Fibonacciโ€™s number using the code above, things start to break*.* The reason it broke is because our recursive call still calculates the value of the previous functions even when it has calculated it before

  • Example of how it looks like

Gif courtesy of geeksforgeeks https://www.geeksforgeeks.org/javascript-memoization/#:~:text=Importance of Memoization When you cache an answer from memory.

This is where memoization comes in, so letโ€™s optimise it by returning the cache result of our function instead!

Weโ€™ve introduced an additional param to our function called cache and set the value of it to be an empty array if it doesnโ€™t exists.

This stores the previous value of our result and we send the value to our function so that the program doesnโ€™t need to recompute the value again and again and again and again and again.

This will lay the foundation of how memoization works

React.memo

React Top-Level API - React

One of the API that React provides us that helps with performance is React.memo

React.memo is a higher order component and is used as a performance optimisation by memoizing the result

Take for example a button component below

When my App loads, it will render the Button component

Inside my App, if a user types in the input, it will call the function changeText which will call setText and re-render the components

This might seem minor but on a page with many components, we donโ€™t really want a simple Button component to re-render every time a user types right?

This is where React.memo comes in and is a handy way to optimise unnecessary re-renders

By simply wrapping it in React.memo whenever state changes in my App it will not cause a re-render of the Button component! React will skip rendering the component, and reuse the last rendered result.

๐Ÿ’ก A good way to know when to use it for a component is if the component renders the same result given the same props.

useMemo

Returns a memoized value.

This is the equivalent of React.memo that we encountered except that itโ€™s for values.

Below, we changed the Button component to track a count state. Every-time we click the button, we will add 1 to count. We also implemented the fibonacci sequence from above and plugged it into our App

Notice how whenever we clicked on the Button component to add to the counter, our whole App re-renders again and we will have to recalculate our fibonacci number even though the num state didnโ€™t change.

This is an issue especially if the number is big and our function will be really expensive to compute.

Luckily, this is where useMemo comes in and optimises the performance by skipping the calculation part if the input hasnโ€™t changed! In essence, we memoized the result and from useMemo and we keep track of the input of num in the dependency array.

useCallback

If you ran the code above, youโ€™ll actually realise that our previous Button component that we wrapped in React.memo actually is re-rendering again.

Hmm, but nothing changed right? If you look a little closer, this time we are passing in a prop called handleOnClick which takes in a function.

Well if you passed in a primitive value such as a string or an integer, it wonโ€™t cause a re-render if the value remained the same, but why did passing in a function caused an issue?

The real reason is because functions are compared by reference and not by value. The code below illustrates this example:

Whenever React re-renders, the function will be re-generated on every single render, producing a unique function each time. That is to say that the function we passed on to our Button component has โ€œchangedโ€ on each render, causing it to re-render again!

Fortunately, React has provided us with the useCallback hook that will allow us to keep that function that we created to be the same handleCounterClick every time.

The Button component now will not re-render unnecessarily and we have optimised our App to be more performant.

Conclusion

Despite learning how to make our app more performant, itโ€™s not always necessary to use these hooks. Remember that they are there as tools to help when things feel sluggish or slow. React is a very powerful library that knows how to optimise itself even without using the hooks that we have learned.

Hereโ€™s a great article on when you should use the hooks that we have learned by Kent C. Dodds

When to useMemo and useCallback

Resources

If Hereโ€™s a really great resource by Josh W Comeau that takes deep dive into what we just went through.

Understanding useMemo and useCallback