You’re overusing useMemo: Rethinking Hooks memoization
Tags: react, javascript, webdev
Published: 2019-08-14 19:05:01

---
title: You’re overusing useMemo: Rethinking Hooks memoization
published: true
tags: react, javascript, webdev
canonical_url: https://blog.logrocket.com/rethinking-hooks-memoization/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/0cxko0ihlyb7a2o4djm5.jpeg
---
**Written by [Ohans Emmanuel](https://blog.logrocket.com/author/ohansemmanuel/)**✏️

In my experience, there are two main categories where I’ve found `useMemo` to be irrelevant, overused, and likely harmful to the performance of your application.

The first category is easy to reason about; however, the second category is quite subtle and easily ignored. If you’ve used Hooks in any serious production app, then you’ve likely been tempted to use the `useMemo` Hook in one of these two categories.

I’ll show you why these are unimportant and likely hurting the performance of your application, and more interestingly, I’ll show you my recommendations on how _not_ to overuse `useMemo` in these use cases.

Shall we get started?

## Where not to use `useMemo`

The classifications, for the sake of learning purposes, will be called Lions and Chameleons.

![Category A And Category B: Lions And Chameleons](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2019/08/lions-and-chameleons.png?resize=730%2C241&ssl=1)

Ignore the distracting classification monikers and hang on!

Your immediate reaction when confronted by a lion is to run away, protect your heart from being ripped apart, and live to tell the story later. There’s no time for chitter-chatter.

This is category A. They are lions, and your reaction should be to run away from these.

Let’s start with these before looking at the more subtle chameleons.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

### 1. Same reference and inexpensive operations

Consider the example component below:

```jsx
/** 
  @param {number} page 
  @param {string} type 
**/
const myComponent({page, type}) { 
  const resolvedValue = useMemo(() => {
     getResolvedValue(page, type)
  }, [page, type])

  return <ExpensiveComponent resolvedValue={resolvedValue}/> 
}
```

In this example, it’s easy to justify the writer’s use of `useMemo`. What goes through their mind is they don’t want the `ExpensiveComponent` to be re-rendered when the reference to `resolvedValue` changes.

While that’s a valid concern, there are two questions to ask to justify the use of `useMemo` at any given time.

First, is the function passed into `useMemo` an expensive one? In this case, is the `getResolvedValue` computation an expensive one?

Most methods on JavaScript data types are optimized, e.g. `Array.map`, `Object.getOwnPropertyNames()`, etc. If you’re performing an operation that’s not expensive (think Big O notation), then you don’t need to memoize the return value. The cost of using `useMemo` [may outweigh](https://kentcdodds.com/blog/usememo-and-usecallback) the cost of reevaluating the function.

Second, given the same input values, does the reference to the memoized value change? For example, in the code block above, given the `page` as `2` and `type` as `"GET"`, does the reference to `resolvedValue` change?

The simple answer is to consider the data type of the `resolvedValue` variable. If `resolvedValue` is a `primitive` (i.e., `string`, `number`, `boolean`, `null`, `undefined`, or `symbol`), then the reference never changes. By implication, the `ExpensiveComponent` won’t be re-rendered.

Consider the revised code below:

```jsx
/** 
  @param {number} page 
  @param {string} type 
**/
const MyComponent({page, type}) {
  const resolvedValue = getResolvedValue(page, type)
  return <ExpensiveComponent resolvedValue={resolvedValue}/> 
}
```

Following the explanation above, if `resolvedValue` returns a string or other primitive value, and `getResolvedValue` isn’t an expensive operation, then this is perfectly correct and performant code.

As long as `page` and `type` are the same — i.e., no prop changes — `resolvedValue` will hold the same reference except the returned value isn’t a primitive (e.g., an object or array).

Remember the two questions: Is the function being memoized an expensive one, and is the returned value a primitive? With these questions, you can always evaluate your use of `useMemo`.

### 2. Memoizing default state for any number of reasons

Consider the following code block:

```jsx
/** 
  @param {number} page 
  @param {string} type 
**/
const myComponent({page, type}) { 
  const defaultState = useMemo(() => ({
    fetched: someOperationValue(),
    type: type
  }), [type])

  const [state, setState] = useState(defaultState);
  return <ExpensiveComponent /> 
}
```

The code above seems harmless to some, but the `useMemo` call there is absolutely unimportant.

First, out of empathy, understand the thinking behind this code. The writer’s intent is laudable. They want a new `defaultState` object when the `type` prop changes, and they don’t want reference to the `defaultState` object to be invalidated on every re-render.

While these are decent concerns, the approach is wrong and violates a fundamental principle: `useState` will not be reinitialized on every re-render, only when the component is remounted.

The argument passed to `useState` is better called `INITIAL_STATE`. It’s only computed (or triggered) once when the component is initially mounted.

```jsx
useState(INITIAL_STATE)
```

Even though the writer is concerned about getting a new `defaultState` value when the `type` array dependency for `useMemo` changes, this is a wrong judgment as `useState` ignores the newly computed `defaultState` object.

This is the same for lazily initializing `useState` as shown below:

```jsx
/**
   @param {number} page 
   @param {string} type 
**/
const myComponent({page, type}) {
  // default state initializer 
  const defaultState = () => {
    console.log("default state computed")
    return {
       fetched: someOperationValue(),
       type: type
    }
  }

  const [state, setState] = useState(defaultState);
  return <ExpensiveComponent /> 
}
```

In the example above, the `defaultState` init function will only be invoked once — on mount. The function isn’t invoked on every re-render. As a result, the log “default state computed” will only be seen once, except the component is remounted.

Here’s the previous code rewritten:

```jsx
/**
   @param {number} page 
   @param {string} type 
**/
const myComponent({page, type}) {
  const defaultState = () => ({
     fetched: someOperationValue(),
     type,
   })

  const [state, setState] = useState(defaultState);

  // if you really need to update state based on prop change, 
  // do so here
  // pseudo code - if(previousProp !== prop){setState(newStateValue)}

  return <ExpensiveComponent /> 
}
```

We will now consider what I deem more subtle scenarios where you should avoid `useMemo`.

### 3. Using `useMemo` as an escape hatch for the ESLint Hook warnings

![ESLint Hook Warnings](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2019/08/eslint-hook-warnings.png?resize=730%2C134&ssl=1)

While I couldn’t bring myself to read [all the comments](https://github.com/facebook/create-react-app/issues/6880) from people who seek ways to suppress the lint warnings from the official [ESLint plugin for](https://www.npmjs.com/package/eslint-plugin-react-hooks) [Hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks), I do understand their plight.

I agree with Dan Abramov [on this one](https://github.com/facebook/create-react-app/issues/6880#issuecomment-485912528). Suppressing the `eslint-warnings` from the plugin will likely come back to bite you someday in the future.

Generally, I consider it a bad idea to suppress these warnings in production apps because you increase the likelihood of introducing subtle bugs in the near future.

With that being said, there are still some valid cases for wanting to suppress these lint warnings. Below is an example I’ve run into myself. The code’s been simplified for easier comprehension:

```jsx
function Example ({ impressionTracker, propA, propB, propC }) {
  useEffect(() => {
    // 👇Track initial impression
    impressionTracker(propA, propB, propC)
  }, [])

  return <BeautifulComponent propA={propA} propB={propB} propC={propC} />                 
}
```

This is a rather tricky problem.

In this specific use case, you don’t care whether the props change or not. You’re only interested in invoking the `track` function with whatever the _initial_ props are. That’s how impression tracking works. You only call the impression track function when the component mounts. The difference here is you need to call the function with some initial props.

While you may think simply renaming the `props` to something like `initialProps` solves the problem, that won’t work. This is because `BeautifulComponent` relies on receiving updated prop values, too.

![Initial Props And Updated Props Example](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2019/08/props-lint-warning-example.png?resize=730%2C309&ssl=1)

In this example, you will get the lint warning message: “_React Hook useEffect has missing dependencies: ‘impressionTracker’, ‘propA’, ‘propB’, and ‘propC’. Either include them or remove the dependency array_.”

That’s a rather brash message, but the linter is simply doing its job. The easy solution is to use a `eslint-disable` comment, but this isn’t always the best solution because you could introduce bugs within the same `useEffect` call in the future.

```jsx
useEffect(() => {
  impressionTracker(propA, propB, propC)
  // eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
```

My suggestion solution is to use the `useRef` Hook to keep a reference to the initial prop values you don’t need updated.

```jsx
function Example({impressionTracker, propA, propB, propC}) {
  // keep reference to the initial values         
  const initialTrackingValues = useRef({
      tracker: impressionTracker, 
      params: {
        propA, 
        propB, 
        propC, 
    }
  })

  // track impression 
  useEffect(() => {
    const { tracker, params } = initialTrackingValues.current;
    tracker(params)
  }, []) // you get NO eslint warnings for tracker or params

  return <BeautifulComponent propA={propA} propB={propB} propC={propC} />   
}
```

In all my tests, the linter only respects `useRef` for such cases. With `useRef`, the linter understands that the referenced values won’t change and so you don’t get any warnings! **Not even `useMemo` prevents these warnings**.

For example:

```jsx
function Example({impressionTracker, propA, propB, propC}) {

  // useMemo to memoize the value i.e so it doesn't change
  const initialTrackingValues = useMemo({
    tracker: impressionTracker, 
    params: {
       propA, 
       propB, 
       propC, 
    }
  }, []) // 👈 you get a lint warning here

  // track impression 
  useEffect(() => {
    const { tracker, params} = initialTrackingValues
    tracker(params)
  }, [tracker, params]) // 👈 you must put these dependencies here

  return <BeautifulComponent propA={propA} propB={propB} propC={propC} />
}
```

In the faulty solution above, even though I keep track of the initial values by memoizing the initial prop values with `useMemo`, the linter still yells at me. Within the `useEffect` call, the memoized values `tracker` and `params` still have to be entered as array dependencies, too.

I’ve seen people `useMemo` in this way. It’s poor code and should be avoided. Use the `useRef` Hook, as shown in the initial solution.

In conclusion, in most legitimate cases where I really want to silent the lint warnings, I’ve found `useRef` to be a perfect ally. Embrace it.

### 4. Using `useMemo` solely for referential equalities

Most people say to use `useMemo` for expensive calculations and for keeping referential equalities. I agree with the first but _disagree_ with the second. Don’t use the `useMemo` Hook just for referential equalities. There’s only one reason to do this — which I discuss later.

Why’s using `useMemo` solely for referential equalities a bad thing? Isn’t this what everyone else preaches?

Consider the following contrived example:

```jsx
function Bla() {
  const baz = useMemo(() => [1, 2, 3], [])
  return <Foo baz={baz} />
}
```

In the component `Bla`, the value `baz` is memoized NOT because the evaluation of the array `[1,2,3]` is expensive, but because the reference to the `baz` variable changes on every re-render.

While this doesn’t seem to be a problem, I don’t believe `useMemo` is the right Hook to use here.

One, look at the array dependency.

```jsx
useMemo(() => [1, 2, 3], [])
```

Here, an empty array is passed to the `useMemo` Hook. By implication, the value `[1,2,3]` is only computed once — when the component mounts.

So, we know two things: the value being memoized is not an expensive calculation, and it is not recomputed after mount.

If you find yourself in such a situation, I ask that you rethink the use of the `useMemo` Hook. You’re memoizing a value that is not an expensive calculation and isn’t recomputed at any point in time. There’s no way this fits the definition of the term “memoization.”

This is a terrible use of the `useMemo` Hook. It is semantically wrong and arguably costs you more in terms of memory allocation and performance.

So, what should you do?

First, what exactly is the writer trying to accomplish here? They aren’t trying to memoize a value; rather, they want to keep the **reference** to a value the same across re-renders.

Don’t give that slimy chameleon a chance. In such cases, use the `useRef` Hook.

For example, if you really hate the use of the current property (like a lot of my colleagues), then simply deconstruct and rename as shown below:

```jsx
function Bla() {
  const { current: baz } = useRef([1, 2, 3])
  return <Foo baz={baz} />
}
```

Problem solved.

In fact, you can use the `useRef` to keep reference to an expensive function evaluation — so long as the function doesn’t need to be recomputed on props change.

`useRef` is the right Hook for such scenarios, NOT the `useMemo` Hook.

Being able to use the `useRef` Hook to mimic [instance variables](https://reactjs.org/docs/hooks-faq.html#is-there-something-like-instance-variables) is one of the least used super powers Hooks avail us. The `useRef` hook can do more than just keeping references to DOM nodes. Embrace it.

Please remember, the condition here is if you’re memoizing a value just because you need to keep a consistent reference to it. If you need the value to be recomputed based off of a changing prop or value, then please feel free to use the `useMemo` hook. In some cases you could still use `useRef` – but the `useMemo` is mostly convenient given the array dependency list.

## Conclusion

Run away from lions, but don’t let the chameleons fool you. If you allow them, the chameleons will change their skin colors, blend into your codebase, and pollute your code quality.

Don’t let them.

Curious what my stance on advanced Hooks is? I’m working on a video course for advanced Hooks. Sign up and I’ll [let you know](https://forms.gle/Qd6yZC1Lhcosnc2f8) when it’s out!
* * *
**Editor's note:** Seeing something wrong with this post? You can find the correct version [here](https://blog.logrocket.com/rethinking-hooks-memoization/).

## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *
The post [You’re overusing useMemo: Rethinking Hooks memoization](https://blog.logrocket.com/rethinking-hooks-memoization/) appeared first on [LogRocket Blog](https://blog.logrocket.com).