How to Use Memoization in React for Better Performance
Tags: react, memoization, reacthooks, optimization
Published: 2024-05-16 03:38:25

---
title: How to Use Memoization in React for Better Performance
published: true
date: 2024-05-16 03:27:13 UTC
tags: React,Memoization,ReactHooks,optimization
canonical_url: https://blog.nicholasfragiskatos.dev/how-to-use-memoization-in-react-for-better-performance
---

Memoization, not memorization, is a technique commonly used in functional programming to improve performance of an application. To put it simply, memoization is just caching the result of a function, then whenever that function is invoked again, as long the inputs are the same, the cached result can be returned instead of re-running the function. It is important to note that the memoized function would need to be a _pure_ function. That is:

1. The returned value is always the same for an identical set of inputs.

2. The function does note mutate state outside of its scope, i.e., there are no side-effects.

React, which heavily encourages the use of a functional style of programming, naturally provides its own mechanisms for developers to optimize their application through memoization. With `React.memo(...)`, `useCallback(...)`, and `useMemo(...)`, developers can have finer control over their application to prevent unnecessary re-rendering of components or heavy recalculations of data.

## `React.memo(...)`

The purpose of `React.memo(...)` is to prevent unwanted re-rendering of a child component if its props have not changed when the parent component is re-rendered.

### The Problem

Consider this `<Parent/>` component. It contains a button that, when clicked, increments a `total` state variable and it displays the current value of `total`. Lastly, it contains a `<Child/>` component that does not take any props.

```javascript
import { useState } from "react"
import Child from "./Child"

const Parent = () => {

	const [total, setTotal] = useState(0)
	  	
	return (
		<div className="flex flex-col gap-1 w-1/5">
			<h1 className="text-3xl">Parent Component</h1>
			<div>Total: {total}</div>
			<button className="border-2 bg-cyan-600 p-1 border-black rounded-md text-white" onClick={() => setTotal(total + 1)}>Increment Parent Count</button>
			<div className="mt-3" />
			<Child />
		</div>
	)
}

export default Parent
```

The `<Child/>` component only displays how many times it has been rendered. There's a `useRef(...)` paired with a `useEffect(...)` that keeps track of the number of renders for the component, and it displays that count.

```javascript
import { useEffect, useRef } from "react"

const Child = () => {
	console.log("Child Rendered")
	const renderCount = useRef(1)
	
	useEffect(() => {
		renderCount.current = renderCount.current + 1
	})
	
	return (
		<div>
			<div className="text-3xl">Child Component</div>
			<div>Render Count: {renderCount.current}</div>
		</div>
	)
}
export default Child
```

All together these two components create this UI

![Picture of UI](https://cdn.hashnode.com/res/hashnode/image/upload/v1715828304793/b8b1aeeb-fa52-4abd-89e6-6e149c59a67c.png)

Let's see what happens when we press the button to increment the `total` state variable of the parent component.

![Gif showing child component re-rendering when parent re-renders](https://cdn.hashnode.com/res/hashnode/image/upload/v1715828426837/51de88e3-8626-45c6-bd9e-2e6265d32410.gif)

The child component re-renders with the parent component. This seems odd, especially since the child component's props are not changing. In fact, it does not even have any props.

### The Solution

Fortunately, the solution is simple; wrap the child component in `React.memo(Component, proprsComparatorFunction)`. Now when the parent component re-renders the child component does not.

```javascript
import { useEffect, useRef, memo } from "react" // import

const Child = () => {
	...
}

export default memo(Child) // wrapping component
```

![Gif showing how React.memo prevents the child component re-rendering when the parent component re-renders](https://cdn.hashnode.com/res/hashnode/image/upload/v1715828544258/fc31b99c-2b57-45a0-8601-e40e9e7e028b.gif)

#### The Comparator Function

`React.memo(...)` has a [second argument](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) for a comparator function, which wasn't utilized in the example. By default, if no function is specified, then React will do a [_shallow_ comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) of the previous and new prop values. A shallow comparison is fine if using simple data types like comparing numbers or booleans, but if the prop is something more complex like an object, then a custom implementation might be necessary.

## `useCallback(...)`

The purpose of `useCallback(...)` is to memoize a function that's defined in the component. If a function is defined in a component, then it will be recreated during each re-render. `useCallback(...)` can prevent his. The benefit to stopping the recreation of the function is exemplified best when functions are being passed down as props to child components.

### The Problem

Consider the same `<Parent/>` and `<Child/>` components from before with the same `React.memo(...)` solution used to prevent re-renders. Except this time `<Child/>` defines a prop for a callback function, and `<Parent/>` defines a function called `myFunction` and passes it down to `<Child/>`. Also, aside from defining the callback prop, `<Child/>` doesn't even do anything with it.

```javascript
import { useState } from "react"
import Child from "./Child"

const Parent = () => {

	const [total, setTotal] = useState(0)
	
	const myFunction = () => {
		console.log(`NON-Callback function called`)
	}
	
	return (
		<div className="flex flex-col gap-1 w-1/5">
			<h1 className="text-3xl">Parent Component</h1>
			<div>Total: {total}</div>
			<button className="border-2 bg-cyan-600 p-1 border-black rounded-md text-white" onClick={() => setTotal(total + 1)}>Increment Parent Count</button>
			<div className="mt-3" />
			<Child callback={myFunction} />
		</div>
	)
}

export default Parent
```

```javascript
import { memo, useEffect, useRef } from "react"

const Child = ({ callback }: { callback: () => void }) => {

	console.log("Child Rendered")
	const renderCount = useRef(1)
	
	useEffect(() => {
		renderCount.current = renderCount.current + 1
	})
	
	return (
		<div>
			<div className="text-3xl">Child Component</div>
			<div>Render Count: {renderCount.current}</div>
		</div>
	)
}

export default memo(Child)
```

Again, let's see what happens when we change state in the parent component.

![Gif showing the child component re-rendering when the parent component re-renders](https://cdn.hashnode.com/res/hashnode/image/upload/v1715829133757/aa3f67f0-b8e0-43fd-ad1b-5a85e67b2b31.gif)

All of `React.memo(...)`'s hard work is undone. Although, `React.memo(...)` is doing exactly what it's supposed to be doing, but there are two problems:

1. Each re-render of `<Parent/>`, `myFunction(...)` gets recreated completely, and occupies a different space in memory, even though nothing has changed about it.

2. `React.memo(...)` is still doing a shallow comparison, but since the old function prop and the new function prop are not referencing the same spot in memory, it thinks that the prop is changed, so `<Child/>` needs to be re-rendered. Remember functions in JavaScript [are objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions) and objects are compared by reference.

### The Solution

Again, the solution is simple; stop recreating the function and just use the old function by using the `useCallback(fn, dependencyList)` hook. The first argument is the function to define whatever work needs to be done, and the second argument, similar to `useEffect(...)`, is for a list of state variables that `useCallback` will check. As long as none of the state dependencies in the list change, then on every render the same function will be returned instead of creating a new one. Now since the function is the exact same, even from a referential perspective, `React.memo(...)`'s shallow comparison will not trigger a re-render of `<Child/>`

```javascript
import { useCallback, useState } from "react"
import Child from "./Child"

const Parent = () => {

	const [total, setTotal] = useState(0)
	
	const myFunction = useCallback(() => {
		console.log(`Callback function called`)
	}, [])
	
	return (
		<div className="flex flex-col gap-1 w-1/5">
			<h1 className="text-3xl">Parent Component</h1>
			<div>Total: {total}</div>
			<button className="border-2 bg-cyan-600 p-1 border-black rounded-md text-white" onClick={() => setTotal(total + 1)}>Increment Parent Count</button>
			<div className="mt-3" />
			<Child callback={myFunction} />
		</div>
	)
}

export default Parent
```

![Gif showing child component no longer re-rendering when parent component re-renders](https://cdn.hashnode.com/res/hashnode/image/upload/v1715829263314/ec34542e-5203-4c05-8c6a-1307c2717618.gif)

## `useMemo(...)`

Lastly, `useMemo(...)` is used to cache the _result_ of a function. When rendering involves calling some function that produces some value, and the function is doing an expensive calculation but no inputs to the function are changing, then there's no point in the recalculation during each re-render.

### The Problem

Continuing from the last code snippet, let's add a new state variable called `myName`, a function called `uppercaseName`, and then in the UI let's add a text field and a `<p>` tag to display the uppercase name.

```javascript
import { useCallback, useState } from "react"
import Child from "./Child"

const Parent = () => {

	const [total, setTotal] = useState(0)
	const [myName, setMyText] = useState("michael scott")
	
	const myFunction = useCallback(() => {
		console.log(`Callback function called`)
	}, [])
	
	const uppercaseName = () => {
		console.log("Processing Name...")
		return myName.toUpperCase()
	}
	
	return (
		<div className="flex flex-col gap-1 w-1/5">
			<h1 className="text-3xl">Parent Component</h1>
			<div>Total: {total}</div>
			<input className="border-2 p-1" type="text" value={myName} onChange={(e) => setMyText(e.target.value)} />
			<button className="border-2 bg-cyan-600 p-1 border-black rounded-md text-white" onClick={() => setTotal(total + 1)}>Increment Parent Count</button>
			<p>{`Hello, this is ${uppercaseName()}`}</p>
			<div className="mt-3" />
			<Child callback={myFunction} />
		</div>
	)
}

export default Parent
```

![Picture showing UI with text field and paragraph added](https://cdn.hashnode.com/res/hashnode/image/upload/v1715829369433/104562b1-584b-461f-a6eb-d185c101d9b5.png)

Typing something in the text field will display an uppercase version in the paragraph below the button.

![Gif showing what happens when typing in the text field](https://cdn.hashnode.com/res/hashnode/image/upload/v1715829449510/e63d1381-91e0-4bea-b80b-7d6c523f6edb.gif)

However, if we open the console and start using the button to increment the count, we can see the function is being ran each time during the re-render even though the name has not changed.

![Gif showing recalculations on every render](https://cdn.hashnode.com/res/hashnode/image/upload/v1715829533543/0ba6d269-0ec2-470b-b44b-5a9776a5d03c.gif)

### The Solution

We can use `useMemo(...)` to wrap the `uppercaseName` function in a similar way we used `useCallback(...)`. Except this time the result of `useMemo(...)` is a value, and not a function. Like `useCallback(...)`, it takes a dependency list, and as long as the state in the dependency list does not change, the result does not have to be recalculated.

```javascript
import { useCallback, useState } from "react"import Child from "./Child"const Parent = () => { const [total, setTotal] = useState(0) const [myName, setMyText] = useState("michael scott") const myFunction = useCallback(() => { console.log(`Callback function called`) }, []) const uppercaseName = useMemo(() => { console.log("Processing Name...") return myName.toUpperCase() }, [myName]) return ( <div className="flex flex-col gap-1 w-1/5"> <h1 className="text-3xl">Parent Component</h1> <div>Total: {total}</div> <input className="border-2 p-1" type="text" value={myName} onChange={(e) => setMyText(e.target.value)} /> <button className="border-2 bg-cyan-600 p-1 border-black rounded-md text-white" onClick={() => setTotal(total + 1)}>Increment Parent Count</button> <p>{`Hello, this is ${uppercaseName}`}</p> <div className="mt-3" /> <Child callback={myFunction} /> </div> )}export default Parent
```

![Gif showing the calculation no longer being done needlessly](https://cdn.hashnode.com/res/hashnode/image/upload/v1715829646663/127c4287-dc8d-4c8b-8b68-2b64eb38e2e8.gif)

## Over Optimization

After learning about these optimization techniques it may be tempting to start using them everywhere. However that might be overkill for most scenarios. In fact, the React documentation advises to only use these techniques for specific scenarios. However, it is ultimately up to you to decide what is best for your application and when to use what and where to use it.

[Should you add `React.memo` everywhere?](https://react.dev/reference/react/memo#should-you-add-memo-everywhere)

[Should you add `useCallback` everywhere?](https://react.dev/reference/react/useCallback#should-you-add-usecallback-everywhere)

[Should you add `useMemo` everywhere?](https://react.dev/reference/react/useMemo#should-you-add-usememo-everywhere)

## Conclusion

React provides its own mechanisms to developers to optimize their application through memoization.

Through the use of `React.memo(...)` we can limit the number of re-renders of a child component when the parent component re-renders. `React.memo(...)` will memoize the child component and only re-render if its props have changed.

Utilizing `useCallback(...)` we can prevent the unnecessary recreation of functions each time a component gets re-rendered. Furthermore, we saw how this can be used in conjunction with `React.memo(...)` to limit child component re-renders when passing functions down as props.

Lastly, `useMemo(...)` can be used to avoid duplicate recalculations and data processing, especially if it is an expensive operation. As long as dependent state for the calculation does not change, there is no reason to perform the operation.

* * *

Thank you for taking the time to read my article. I hope it was helpful.

If you noticed anything in the article that is incorrect or isn't clear, please let me know. I always appreciate the feedback.