Memoisation in React
Tags: react, webdev
Published: 2021-03-13 03:22:49

---
title: Memoisation in React
published: true
description: How to use React.memo, useMemo & useCallback
tags: react, webdev
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/su682gq45len5edmpzd4.jpg
---

Memoisation is an optimisation technique that caches the result of previous computations so that they can be quickly accessed without repeating the same computation.

React introduces quite a few memoisation functions being `React.memo`, `useMemo` and `useCallback`.

## 1. React.memo

`React.memo` is a higher order component when wrapped around a component, memoises the result of the component and does a **shallow** comparison before the next render. If the new props are the same the component doesn't re-render and uses the memoised result.

By default `memo` does a shallow comparison of props, however, the second argument allows you to define a custom equality check function. From React's official docs:
```js
function MyComponent(props) {
  /* render using props */
}
function areEqual(prevProps, nextProps) {
  /*
  return true if passing nextProps to render would return
  the same result as passing prevProps to render,
  otherwise return false
  */
}
export default React.memo(MyComponent, areEqual);
```

However, if you're looking to do a deep comparison between 2 values and want to take the easy route you can use `isEqual` from `lodash`.

Now let's have a look at this example:

```js
// App.js
import React, { useState } from "react";

import Child from "./Child.js";

const App = () => {
  const [counter, setCounter] = useState(0);
  const [text, setText] = useState("");

  return (
    <div className="App">
      <input
        onChange={(e) => setText(e.target.value)}
        type="text"
        value={text}
      />
      <button onClick={() => setCounter(counter + 1)}>+ 1</button>
      <Child counter={counter} />
    </div>
  );
};

export default App;
```
In this case we have a parent component called `App` which takes in a `<Child />` component.
```js
import React from "react";

const Child = ({ counter }) => {
  console.log("rendering...");

  return <div>Count: {counter}</div>;
};

export default Child;

``` 
If you open up `Console` you will notice that given each keystroke in the input field the `<Child />` component re-renders. Obviously this doesn't have any performance overhead  at this point in time, but imagine if the `Child` component had child components of its own with state. Then you'd trigger a re-render of all components associated with the parent, that'd definitely add overhead to your application.

To prevent child components from unnecessarily re-rendering like that we have to use `React.memo`. All we need to do is wrap our `Child` component in our `memo` and you see that no matter what we type in the input field it doesn't trigger a re-render of the `<Child />` component.

```js
import React, { memo } from "react";

const Child = ({ counter }) => {
  console.log("rendering...");

  return <div>Count: {counter}</div>;
};

export default memo(Child);
```

However, what if we wanted to pass down functions or anything that isn't a primitive value such as objects since `memo` only does a shallow comparison? A shallow comparison in this case means it only checks if the props that you're passing down is referencing the same place in memory. 

So let's say we want to update the `counter` from `<Child />` so we do something like this:
```js
// App.js
import React, { useState } from "react";

import Child from "./Child.js";

const App = () => {
  const [counter, setCounter] = useState(0);
  const [text, setText] = useState("");

  const updateCounterHandler = () => {
    setCounter(counter + 1);
  };

  return (
    <div className="App">
      <input
        onChange={(e) => setText(e.target.value)}
        type="text"
        value={text}
      />
      <button onClick={() => setCounter(counter + 1)}>+ 1</button>
      <Child counter={counter} updateCounter={updateCounterHandler} />
    </div>
  );
};

export default App;

```
and within Child.js:
```js
import React, { memo } from "react";

const Child = ({ counter, updateCounter: pushUpdateCounter }) => {
  console.log("rendering...");

  return (
    <div>
      <strong>Count: {counter}</strong>
      <button onClick={pushUpdateCounter}>Update Counter</button>
    </div>
  );
};

export default memo(Child);

```
However, you will notice that the `<Child />` component still gets rendered whenever we type something into the input field. This is because the `updateCounterHandler` inside `App.js` gets recreated each time the state changes.

So the correct way to handle callback functions with memo is by using `useCallback`.

## 2. useCallback

`useCallback` is a hook that comes with `react` that returns a memoised function. It takes in 2 arguments, the first one being the callback function, the second being an array of dependencies.

So all that needs to be done is wrap `useCallback` around our `updateCounterHandler` function to prevent the `<Child />` component from re-rendering whenever we type in the input field.

```js
const updateCounterHandler = useCallback(() => {
    setCounter(counter + 1);
  }, [counter]);
```

## 3. useMemo

Like `useCallback`, `useMemo` is a hook that takes in a function, however, instead of returning a memoised function it returns a memoised value. This makes it useful when performing heavy calculations.

```js
import React, { memo, useMemo } from "react";

const Child = ({ counter, updateCounter: pushUpdateCounter }) => {
  console.log("rendering...");

  const outputNumber = useMemo(() => {
    let output = 0;

    for (let i = 0; i < 10000000; i++) {
      output++;
    }

    return output;
  }, []);

  return (
    <div>
      <strong>Count: {counter}</strong>
      <div>Output Number: {outputNumber}</div>
      <button onClick={pushUpdateCounter}>Update Counter</button>
    </div>
  );
};

export default memo(Child);
```

Using `useMemo` in the example above, we're able to cache the return value of `outputNumber`, so that we're not recalling the function each time.

---
After learning these techniques, I hope you're able to apply it to where it's truly needed, because **premature optimisation is the root of all evil!** It's about finding the fine line between compromising space and time as speed optimisation techniques such as memoisation eat up space (RAM) in return for a faster time. So always question yourself before optimising your code, "do the performance gains really justify the usage?".