Supercharging your React projects with Memoization
Tags: react, webdev, javascript, cache
Published: 2022-07-04 03:10:38

`useMemo` and `useCallback` are React hooks for memoization. Think of memoization as caching a value so that it does not need to be recalculated. This improves performance. The main difference between `useMemo` and `useCallback` is that `useCallback` returns a memoized function (which just really means a 'cached function') and `useMemo` returns a memoized value. Let's go through these hooks together.

Let's start with `useMemo`

## Using useMemo


One reason to use `useMemo` is to prevent an expensive function from re-rendering unless one of its dependencies update.

### Problem

In this example, we have an expensive function that runs on every render.

When changing the count or adding a todo, you will notice a delay in execution.

```
import { useState } from "react";
import ReactDOM from "react-dom";

const App = () => {
  const [count, setCount] = useState(0);
  const [todos, setTodos] = useState([]);
  const calculation = expensiveCalculation(count);

  const increment = () => {
    setCount((c) => c + 1);
  };
  const addTodo = () => {
    setTodos((t) => [...t, "New Todo"]);
  };

  return (
    <div>
      <div>
        <h2>My Todos</h2>
        {todos.map((todo, index) => {
          return <p key={index}>{todo}</p>;
        })}
        <button onClick={addTodo}>Add Todo</button>
      </div>
      <hr />
      <div>
        Count: {count}
        <button onClick={increment}>+</button>
        <h2>Expensive Calculation</h2>
        {calculation}
      </div>
    </div>
  );
};

const expensiveCalculation = (num) => {
  console.log("Calculating...");
  for (let i = 0; i < 1000000000; i++) {
    num += 1;
  }
  return num;
};

ReactDOM.render(<App />, document.getElementById('root'));
```

### Solution

To fix this performance issue, we have to find a way to prevent the rerender of the expensive function. To do this we'll have to memoize the expensive function. This is done by wrapping the expensive function call with `useMemo`.

The `useMemo` Hook accepts a second parameter to declare dependencies. The expensive function will only run when its dependencies have changed.

In the following example, the expensive function will only run when count is changed and not when todo's are added.


```
import { useState, useMemo } from "react";
import ReactDOM from "react-dom";

const App = () => {
  const [count, setCount] = useState(0);
  const [todos, setTodos] = useState([]);
  const calculation = useMemo(() => expensiveCalculation(count), [count]);

  const increment = () => {
    setCount((c) => c + 1);
  };
  const addTodo = () => {
    setTodos((t) => [...t, "New Todo"]);
  };

  return (
    <div>
      <div>
        <h2>My Todos</h2>
        {todos.map((todo, index) => {
          return <p key={index}>{todo}</p>;
        })}
        <button onClick={addTodo}>Add Todo</button>
      </div>
      <hr />
      <div>
        Count: {count}
        <button onClick={increment}>+</button>
        <h2>Expensive Calculation</h2>
        {calculation}
      </div>
    </div>
  );
};

const expensiveCalculation = (num) => {
  console.log("Calculating...");
  for (let i = 0; i < 1000000000; i++) {
    num += 1;
  }
  return num;
};

ReactDOM.render(<App />, document.getElementById('root'));
```


Let's look at useCallback!!!


## Using useCallback


The main difference between useMemo and useCallback is that useCallback returns a memoized function (which just really means a 'cached function') and useMemo returns a memoized value.

( for the code example for `useCallback`, we'll use the exact example used for the `useMemo` but the expensive function would be in another component called `ExpensiveCalc.js`.)

```

import { useState } from "react";
import ReactDOM from "react-dom";

const App = () => {
  const [count, setCount] = useState(0);
  const [todos, setTodos] = useState([]);

  const increment = () => {
    setCount((c) => c + 1);
  };

  const expensiveCalculation = useCallback((num) => {
  console.log("Calculating...");
  for (let i = 0; i < 1000000000; i++) {
    num += 1;
  }
  return num;
  }, [count]);	

  return (
    <div>
      <div>
        <h2>My Todos</h2>
        {todos.map((todo, index) => {
          return <p key={index}>{todo}</p>;
        })}
        <button onClick={addTodo}>Add Todo</button>
      </div>
      <hr />
      <div>
        <ExpensiveCalc count={count} increment={increment}/>
      </div>
    </div>
  );
};




ReactDOM.render(<App />, document.getElementById('root'));


```

`ExpensiveCalc.js`

```
import { memo } from "react";

const ExpensiveCalc = ({ count, increment }) => {
  console.log("child render");
  return (
    <>
      <h2>Count</h2>
      Count: {count}
      <button onClick={increment}>+</button>
    </>
  );
};

export default memo(ExpensiveCalc); //memo will cause React to skip rendering a component if its props have not changed.
```