linkedin Skip to Main Content
 

A Guide To Using React’s useMemo Hook

Development

React is one of the most used JavaScript libraries, and it is used for building fast and interactive user interfaces. But at times, implementing certain functions, fetching massive data from external sources, and lots more make it difficult for your React applications to run fast, thereby reducing performance.

In this guide, you will learn what the useMemo hook means, how you can use it to optimize your React applications, and when you should use it. You will also get to know other options you can use in other situations, so you don’t use the useMemo hook when you should not.

The useMemo hook memoizes values in your React application. These values usually come from expensive functions, and you use the useMemo hook to store the value, so the expensive function doesn’t always run.

💡 Expensive functions are those that require a significant amount of time and resources (memory or processing power) to execute.

What is memoization?

Memorization is a powerful optimization technique that caches computation results to retrieve them from the cache when needed. Hence, this speeds up your code because you don’t need to repeat unnecessary computations of expensive functions.

An excellent example is the Fibonacci sequence. Each number in the Fibonacci sequence, or “Fibonacci number,” is the sum of the two numbers in the sequence before it. This sequence starts with 0 and 1. The third number is the addition of the first two in the sequence, which are 0 and 1, to become 1, and so on, as seen below:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...

If you are to write a program to solve this, you can use recursion because each element is the sum of the previous two elements:

const fibonacci = (n) => { if (n <= 1) return 1 return fibonacci(n - 1) + fibonacci(n - 2) }
Code language: JavaScript (javascript)

The above uses recursion; meaning it will continue to call a copy of itself until its value is less than one, then it returns one. To understand this better, here is an illustration of how this will work if you want to calculate for fibonacci(5).

A visual tree with branches displaying the Fibonacci sequence from 0 to 5. The tree is used to visually represent the fibonacci sequence and its progression.

In the above, you will notice that fibonacci(3), fibonacci(2), fibonacci(1), and fibonacci(0) are calculated several times, which can affect the speed when you have to do more expensive calculations or if you want to get the 35th value. 

You can fix this by introducing memorization, which lets you store the values of these positions and only return them instead of having to recompute them several times:

const fibonacci = (n, memoValues) => { memoValues = memoValues || {} if (memoValues[n]) return memoValues[n] if (n <= 1) return 1 return memoValues[n] = fibonacci(n-1, memoValues) + fibonacci(n-2, memoValues) }
Code language: JavaScript (javascript)

The code above checks if there is a cached value; if so, it prevents recursion. If there is no cached value, it calculates the recursion and saves it to the cache immediately before returning the value. To understand this better, here is what the illustration for fibonacci(5) now looks like:

A visual tree displaying the Fibonacci sequence from 0 to 5, but with memoization applied to improve calculation performance and efficiency.

💡 Cache is a type of temporary storage that a computer or program uses to hold data that is likely to be accessed again in the near future.  

There is no repetition, and the functions with blue backgrounds are getting “memoized values” instead of recomputing them.

Now that we have a basic understanding of memoization, let’s see how it can help you store the values of expensive computations in your React application for better performance.

React re-rendering problem

In React, whenever a state or prop updates or value changes, the component(s) re-renders, meaning all expensive functions, computations, and API requests are re-rendered, leading to slow performance. It doesn’t matter if the state has nothing to do with the other components; when a state changes in the parent component, every other component will re-render.

To explain this, let’s create a react application that involves expensive calculations like the factorial calculation:

import { useState } from 'react'; import './App.css'; const calculateFibonacci = (n) => { if (n < 2) { return n; } return calculateFibonacci(n - 1) + calculateFibonacci(n - 2); }; export const App = () => { const [number, setNumber] = useState(0); const [name, setName] = useState(''); const fibonacciResult = calculateFibonacci(number); return ( <div className="container"> <h1>Fibonacci Calculator</h1> <input type= "number" value={number} onChange={(e) => setNumber(e.target.value)} /> <p className="bold"> This Fibonacci result for <span>{number}</span> is{' '} <span>{fibonacciResult}</span> </p> <div className="container2"> <label>Please Enter your name to see that it is slow:</label> <input type= "text" value={name} onChange={(e) => setName(e.target.value)} /> <p className="bold">{name || 'The name will appear here...'}</p> </div> </div> ); };
Code language: JavaScript (javascript)

In the application above, there are two states, one to store the number from the input form while the second to store the name. Basically, what this application does is that any number you input into the number field will calculate the factorial using the external function placed outside the component:

const calculateFibonacci = (n) => { if (n < 2) { return n; } return calculateFibonacci(n - 1) + calculateFibonacci(n - 2); };
Code language: JavaScript (javascript)

This expensive function is called inside the component, and the number state is passed into it, so it calculates the value, and you can then display it in the application:

const fibonacciResult = calculateFibonacci(number);
Code language: JavaScript (javascript)

But the problem here is that whenever a state is updated, this function will always run even when the state does not affect the number state. For example, there is a name state; whenever you type into the name input field, it will always re-render the component, thereby always running this expensive function.

Let’s test this. If you pass a number from 35 upwards, you will begin to notice that it takes some seconds to calculate. This is because it will need to do a lot of recursions, as you can see in the GIF below.

That is not the problem, but the problem is that when you try to do anything on your application that involves a state, it will take time because the application will re-render. Meaning the Fibonacci function will always calculate the value for the number passed. 

As you see below, when I type the name, even though it shows the component re-renders, it takes time for the name to appear because the function is calculated for each render.

To fix this, you will introduce the useMemo hook to store the value of expensive calculations and operations and stop them from continually rendering even when the component re-renders. useMemo is an official hook you can import from react.

import { useState, useMemo } from 'react'; // ... const fibonacciResult = useMemo(() => calculateFibonacci(number), [number]);
Code language: JavaScript (javascript)

At this point, when you update other states or props in your component, as long as the number state does not change, the value will be stored. The expensive function will not compute, making the performance of your application fast:

Understanding the useMemo hook

The useMemo hook is one of the many built-in react hooks that allow you to memoize values in React applications. It is used basically in two situations which are:

  • When computing expensive operations
  • For referential equality

Overall, it memoizes computed values in a React component so that those values will not be recomputed when the component re-renders. This is the general syntax of the useMemo hook:

const memoizedValue = useMemo(() => { return somevalue }, [...dependencies])
Code language: JavaScript (javascript)

The useMemo hook receives two arguments and is very similar to the `useEffect` hook. The first argument is a callback function, which returns the value, and the second is an array of dependencies. When any of the values of any dependencies changes, a re-computation happens, and then the value is memoized again, which triggers the expensive operation. Just as you have seen in the previous example:

const fibonacciResult = useMemo(() => {     return calculateFibonacci(number); }, [number]);
Code language: JavaScript (javascript)

Referential equality

In JavaScript, we have primitive and non-primitive data types. When you make an equality on primitive data types, they are checked based on their values, while for non-primitive data types like object and array, equality is based on references.

// primitive let a = 5; let b = 5; console.log(a === b); // true // non-primitive let a = {name: "John Doe"}; let b = {name: "John Doe"}; console.log(a === b); // false
Code language: JavaScript (javascript)

Arrays and objects will always return false even though they are equal because their equality is based on references, meaning:

let a = {name: "John Doe"}; let b = {name: "John Doe"}; console.log(a.name === b.name); // true
Code language: JavaScript (javascript)

When objects or functions are declared within your component, they will always be redeclared whenever your component re-renders. Let’s confirm this in this example.

import { useState, useEffect } from 'react'; import './App.css'; export const App = () => { const [name, setName] = useState(''); const [darkTheme, setDarkTheme] = useState(false); const themeStyles = { backgroundColor: darkTheme ? '#333' : '#FFF', color: darkTheme ? '#FFF' : '#333', }; useEffect(() => { console.log('Theme changed'); }, [themeStyles]); return ( <div className="container" style={themeStyles}> <div className="container2"> <label>Please Enter your name to see that it is slow:</label> <input type= "text" value={name} onChange={(e) => setName(e.target.value)} /> <p className="bold">{name || 'The name will appear here...'}</p> </div> <div className="toggle"> <button onClick={() => setDarkTheme((prevDark) => !prevDark)}> Toggle Theme </button> </div> </div> ); };
Code language: JavaScript (javascript)

Suppose you have an object that contains styles; you want to use these styles to control the dark mode and light mode display of your application:

const themeStyles = { backgroundColor: darkTheme ? '#333' : '#FFF', color: darkTheme ? '#FFF' : '#333', };
Code language: JavaScript (javascript)

A darkTheme state is created and has a default value of false. When the button is clicked, the value changes to true, and then the color will change based on the themeStyles object. But the issue is that whenever the component re-renders, this object is redeclared. Let’s track this by introducing a useEffect hook and passing themeStyles as its dependency so it logs out a message whenever the themeStyles is redeclared:

useEffect(() => { console.log('Theme changed'); }, [themeStyles]);
Code language: JavaScript (javascript)

Whenever the name state changes, you will notice the object is redeclared:

You can fix this with the useMemo hook by memoizing the themeStyles object so it only redeclares when the darkTheme state changes:

const themeStyles = useMemo(() => { return { backgroundColor: darkTheme ? '#333' : '#FFF', color: darkTheme ? '#FFF' : '#333', }; }, [darkTheme]);
Code language: JavaScript (javascript)

With this, the useEffect hook will only run when the darkTheme state changes.

useMemo best practices

You now know what the useMemo hook does and how it helps optimize your React application. This doesn’t mean you should memoize all values in your React application; instead, you should only memoize values forexpensive functions and referential equality.

The primary goal is to avoid recomputing expensive functions and operations during re-render and resolve reference conflicts between renders It is essential to know that this hook caches these values; thereby, unnecessary memoizing values will lead to using up more memory which can hurt your application.

In React, you can use other hooks and methods to memoize other aspects of your code to ensure optimization. You can use the useCallback hook to memoize expensive functions. For example, the calculateFibonacci function that was placed outside the component, you can decide to place it within the component but wrap it with the useCallback hook:

const calculateFibonacci = useCallback((n) => { if (n < 2) { return n; } return calculateFibonacci(n - 1) + calculateFibonacci(n - 2); }, []);
Code language: JavaScript (javascript)

You can also use React Memo to memoize the rendered output of a component by wrapping around it, thereby avoiding unnecessary renderings. 

const myComponent = React.memo((props) => { /* render using props */ }); export default myComponent;
Code language: JavaScript (javascript)

Run the sandbox to view the memoized function in action:

Conclusion

In this guide, you have learned what the useMemo hook is about, how it works, and when you should use it—knowing when and where you should apply memorization and the specific method to use is essential.

Thanks for reading!


Hi, I’m Elijah, a technical writer, and software developer, actively sharing all I’ve learned through writing. Follow me on Twitter if you enjoy programming tips and memes.