Why I almost always`useMemo` and `useCallback` 🤯
Tags: react
Published: 2020-01-16 10:07:39

> When your app starts to get slow, it's not a bad idea to start looking into optimizing your code.

This is a sentiment most of us follow (myself included) to avoid falling into the trap of _premature optimization_.

When I first started using React with hooks, I was very much in the mindset that the memoization hooks (`useMemo` and `useCallback`) could be spared for this reason. Over time however, [after](https://github.com/andyrichardson/fielder) [building](https://github.com/andyrichardson/tipple) [libraries](https://github.com/FormidableLabs/urql) and applications that use hooks, I've found it almost always makes sense to memoize your code....

Here's why hooks are more than just a performance optimization.

## What is a performance optimization

When we optimize code, the intention is to reduce the cost (time or resource usage). Particularly when we're optimizing functions or sections of our app, we don't expect the _functionality_ to change, only the _implementation_.

The below is an example of a hook we which keeps the same functionality but changes it's implementation.


```jsx
// Before optimization
const useArrayToObject = (array) => {
  return array.reduce((obj, value) => ({ [value]: true }), {});
}

// After optimization
const useArrayToObject = (array) => {
  const newCollection = {};

  for (let value in array) {
    newCollection[value] = true;
  }

  return newCollection
}
```

### `useMemo` as a performance optimization

Now consider we find ourselves using this hook, and despite our prior optimization, we find that we need to further reduce it's cost. 

As you can probably guess, we can make use of `useMemo` to ensure that the we only run our expensive operation whenever the input argument changes


```jsx
const useArrayToObject = (array) => {
  return useMemo(() => {
    const newCollection = {};

    for (let value in array) {
      newCollection[value] = true;
    }

    return newCollection
  }, [array])
}

```

We merge the changes with confidence that our new optimization has solved the problem, only to hear later on that it's caused a new bug... but how?

### The functional impact of `useMemo`

Despite intending to make a performance-only optimization by memoizing our hook, we've actually changed the way our app _functionally_ works.

_This issue can work both ways - either by adding memoization (sometimes inevitable) or removing it._

Here's the component which was affected by our change

```jsx
const MyComponent = ({ array, dispatch, ...otherProps}) => {
  const collection = useArrayToObject(array);

  useEffect(() => {
    console.log('Collection has changed'); // Some side effect
  }, [collection])

  // ...
}
```

Unlike in the first example, the performance optimizations we have made to the _internals_ of our hook have now changed how consuming components _function_.

## Communicating change

The way in which changes _cascade_ in React hooks is incredibly useful for making a reactive application. But, failing to communicate these changes up front, or modifying when these changes are communicated at a later date, can lead to lost (as in our example) or unintentional reactions elsewhere in your application.

The larger your application and the higher up in your component tree the modifications are, the larger the impact.

### Addressing these issues

So now that you understand that `useMemo` does more than just optimize performance, here's my suggestion

> just get into the habit of memoizing values.

Most are not going to notice the performance impact of additional equality checks incited by over-memoizing; and knowing that change events signalled by values coming from props or hooks can be trusted as actual changes, is valuable.

**Update:** I've added an [example reproduction here](https://codesandbox.io/s/example-useeffect-requirement-vtec7) demonstrating the functional impact of `useMemo`