React useMemo
Tags: react, javascript, webdev
Published: 2023-11-10 12:58:37

useMemo is a React Hook that allows you to memoize the result of a calculation.

Memoization caches the result of a calculation to avoid recalculating it every time it is needed. 

Memoization can improve performance by reducing the number of times expensive calculations need to be performed. 

[React.dev](https://react.dev/reference/react/useMemo#usememo) shows an example of useMemo.

```
import { useMemo } from 'react';

const cachedValue = useMemo(calculateValue, dependencies)
```

where 
- `calculateValue` is the function calculating the value to cache. The function should be a pure function and should take no arguments. It can return any type.
- `dependencies` is an array of all the values used inside `calculateValue`.

# useMemo In React Lifecycle

First, React calls useMemo during the initial render, on component mount. The value returned from the `calculatedValue` function is stored in a variable (cachedValue in the example above).

Then, for all the next renders, React has a choice:

1. If the dependencies have not changed, use the same value. 
2. If any dependency changes, call calculateValue and cache the newly returned value so it can be reused later. 

If the component re-renders for other reasons, useMemo ignores the `calculateValue`  function and uses the cached value.

## Examples
### 1. Separate Components & Push State Down
Sometimes, you can avoid memoization altogether by pushing the state down, as suggested by [Josh Comeau](https://www.joshwcomeau.com/react/usememo-and-usecallback/#an-alternative-approach-3). 

The reasoning goes like this.
If you have two unrelated components, they can manage their own state independently when there is no reason to lift the state up.

In so doing, the two components are independent. In other words, the rendering and re-rendering of ComponentOne doesn't affect ComponentTwo, and vice-versa.

If the heavy computation happens in one component, the re-rendering of the other component doesn't trigger heavy computations again.

In this [example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-vwjbyk?file=src%2FApp.tsx), I am using the Clock component from [Josh Comeau's article](https://www.joshwcomeau.com/react/usememo-and-usecallback/#an-alternative-approach-3), and I am fetching data from jsonplaceholder in a second component called Users.

Since Clock and Users are independent of each other, React re-renders the Clock component every second but not the Users component, which contains an expensive HTTP request. 

```tsx
// App.tsx
...

export const App: FC<{ name: string }> = ({ name }) => {
  return (
    <div>
      <h1>Hello {name}!</h1>
      <Clock />
      <Users />
    </div>
  );
};
```
This solution is also recommended on react.dev: "_Prefer local state and don’t lift state up any further than necessary._" 

However, in some cases, it is not possible to push the state down. But maybe you can lift the content up, as suggested by Dan Abramov in [Before you memo()](https://overreacted.io/before-you-memo/).

### 2. Lift Content Up & Use Children Prop
Here is the example of Dan Abramov, where the ExpensiveTree components shouldn't be re-rendered if possible.

```tsx
export default function App() {
  let [color, setColor] = useState('red');
  return (
    <div style={{ color }}>
      <input value={color} onChange={(e) => setColor(e.target.value)} />
      <p>Hello, world!</p>
      <ExpensiveTree />
    </div>
  );
}
```

A possible solution is to "_split the App component in two. The parts that depend on the color, together with the color state variable itself, have moved into [a new component called] ColorPicker_"

```tsx
// ColorPicker

function ColorPicker({ children }) {
  let [color, setColor] = useState("red");
  return (
    <div style={{ color }}>
      <input value={color} onChange={(e) => setColor(e.target.value)} />
      {children}
    </div>
  );
}
```
"_The parts that don’t care about the color stayed in the App component and are passed to ColorPicker as JSX content, also known as the children prop._"

So the restructured App component becomes
```tsx
// App.tsx

...
export default function App() {
  return (
    <ColorPicker>
      <p>Hello, world!</p>
      <ExpensiveTree />
    </ColorPicker>
  );
}
```
"When the color changes, ColorPicker re-renders. But it still has the same children prop it got from the App last time, so React doesn’t visit that subtree.
And as a result, <ExpensiveTree /> doesn’t re-render."

This solution is also recommended by react.dev: "_When a component visually wraps other components, let it accept JSX as children. This way, when the wrapper component updates its own state, React knows that its children don’t need to re-render._"

If these solutions don't work, we have to resort to memoization.

### 3. Memoize Components
Let's go back to the first example where we pushed the state down.

If the Clock component logic cannot be pushed down, it remains in the App component. React re-renders the App component (and all children) every time a change occurs (every second). You can find this example on this [StackBlitz](https://stackblitz.com/edit/stackblitz-starters-f7ke4j?file=src%2FUsers.tsx).


```tsx
// App.tsx
...

// Use useMemo around Users
export const App: FC<{ name: string }> = ({ name }) => {
  const time = useTime();

  return (
    <div>
      <h1>Hello {name}!</h1>
      <p>{format(time, 'hh:mm:ss a')}</p>
      <Users />
    </div>
  );
};
```
To keep Users from re-rendering and triggering a fetch request every second we can wrap Users around useMemo before exporting it.

```tsx
// Users.tsx

...
const Users: FC = () => { ... }

export default React.memo(Users);
```

This is slightly different from the `useMemo` syntax we saw above. 

```
const cachedValue = useMemo(calculateValue, dependencies)
```
That is because we are using `memo` and not `useMemo`. 
The main difference between memo and useMemo is that memo is a higher-order component (HOC) and useMemo is a React Hook.

- **memo** is used to memoize a React component, which means that it caches the output of the component and only re-renders it if its props have changed. This can be useful when a component's rendering is expensive, and you want to avoid unnecessary re-renders.

- **useMemo** is used to memoize the result of a function inside a component. This can be useful when you need to perform an expensive calculation, and you want to avoid recalculating it every time the component re-renders.


![Memo vs useMemo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1rl4b1s6qfsesgsxy6yd.png)



### 4. Memoize Components And Change Props
There is no array of dependencies in `memo`. So what if we need to trigger the expensive operation again? 

If we are memoizing a component, the component will only be re-rendered if its props change. 

So here is a [memoized component with changing props](https://stackblitz.com/edit/stackblitz-starters-qfwac8?file=src%2FApp.tsx).

First, we added three buttons so you can choose what data you want to fetch:

Once you click a button, the selected item will be saved in the `item` variable and passed to the Users component

```tsx
// App.tsx

...
export const App: FC<{ name: string }> = ({ name }) => {
  const [item, setItem] = useState<Items>('albums');
  const time = useTime();

  return (
    <div>
      <h1>Hello {name}!</h1>
      <p>{format(time, 'hh:mm:ss a')}</p>

      <button onClick={() => setItem('users')}>Users</button>
      <button onClick={() => setItem('posts')}>Posts</button>
      <button onClick={() => setItem('albums')}>Albums</button>

      <Users item={item} />
    </div>
  );
};
```

Second, we changed the code in Users to use the prop passed in from App.tsx.

The new prop is called `item` and is of type Items (you can see all types I used on StackBlitz).

The string assigned to `item` is then used to fetch the correct data from jsonplaceholder. Fetched data is saved in `items` and used in the template to render a list of items in the Users component (at this point we should rename the component because it can render other things).

```tsx
// Users.tsx

const Users: FC = ({ item }: { item: Items }) => {
  const [items, setItems] = useState<User[] | Album[] | Post[]>(null);

  useEffect(() => {
    fetch(`https://jsonplaceholder.typicode.com/${item}`)
      .then((response) => response.json())
      .then((data) => setItems(data));
  }, [item]);

  console.log(items);

  return (
    <div>
      {items &&
        items.map((item: User | Album | Post) => (
          <p key={item.id}>
            {item.name} {item.title}
          </p>
        ))}
    </div>
  );
};

export default React.memo(Users);
```
Note that when you look at the console, you will see two logs every time you select an item. This is because fetching data is an asynchronous operation.
The first log will print the data that is already in `items` (the old data) and the second log will print the newly fetched data.

This works fine as long as `item` is not a collection (e.g. not an array or object). 
The problem with arrays is that "every time React re-renders, we're producing a brand new array. They're equivalent in terms of value, but not in terms of reference."
In other words, they are using two different spaces in your computer's memory.

So, if `item` is an array (let's call it itemArray), we can memoize it as follows:

```tsx
const itemArray = React.useMemo(() => {
  return [
    { id: 1, value: 'users' },
    { id: 2, value: 'posts' },
    { id: 3, value: 'albums' },
  ];
}, []);
```

This closes the circle by going back to the initial `useMemo` syntax we started with.

However, I encourage you to read a more comprehensive explanation about [Preserved References](https://www.joshwcomeau.com/react/usememo-and-usecallback/#use-case-two-preserved-references-4)

## Summary
Memoization caches the result of a calculation to avoid recalculating it every time it is needed. 

Use the HOC memo to memoize components and useMemo to memoize the result of a function.

Before using memoization, try to push the state down or lift the content up. 

- Push state down ([link](https://stackblitz.com/edit/stackblitz-starters-vwjbyk?file=src%2FApp.tsx))
- Lift Content ([link](https://stackblitz.com/edit/stackblitz-starters-j3acyh?file=src%2FApp.tsx))

In alternative use memoization.
- Memoize component ([link](https://stackblitz.com/edit/stackblitz-starters-f7ke4j?file=src%2FUsers.tsx))
- Memoize component and change props ([link](https://stackblitz.com/edit/stackblitz-starters-qfwac8?file=src%2FApp.tsx))

As a rule of thumb, 
1. Only memoize values that are expensive to calculate.
2. Don't memoize values that are frequently updated.

### Nice Reads
- [How to tell if a calculation is expensive? ](https://react.dev/reference/react/useMemo#how-to-tell-if-a-calculation-is-expensive)