React.useMemo and when you should use it

react usememo

As your application scales, performance issues become more and more evident. While React is very well optimized and fast out-of-box, it is important to know the instruments it provides to make your code even faster. One of such instruments is React.useMemo hook and its sidekick, React.useCallback.

What problem does useMemo solve?

useMemo is a React hook that memorizes the output of a function. That is it. useMemo accepts two arguments: a function and a list of dependencies. useMemo will call the function and return its return value. Then, every time you call useMemo again, it will first check if any dependencies have changed. If not, it will return the cached return value, not calling the function. If they have changed, useMemo will call the provided function again and repeat the process.

This should remind you of the useEffect hook: both useMemo and useEffect accept lists of dependencies. The only difference is that useEffect is intended for side-effects (hence the name), while functions in useMemo are supposed to be pure and with no side-effects.

When should you use it?

Firstly, it is important to note that your code must not depend on useMemo. In other words, you should be able to replace useMemo calls with direct function calls and not change anything in the application behavior, except the performance. The easiest way to do it is to write code without useMemo first, then add as needed.

To understand useMemo and when should you use it, check out this example. Firstly, look at this code without useMemo:

This small app will let you enter your name and a number. It will then greet you and display numbers from the Fibonacci sequence. If you run it, you will notice that both the NameDisplay component and the FibDisplay will rerender (and run the expensive computation) if we change the name or the number. This is unacceptable and this is how to fix it:

Firstly, notice the use of useMemo in FibDisplay. We wrapped the expensive computation in a function that will run only when the length changes. The component will still rerender, but the expensive computation will not run unless required.

Secondly, notice that the NameDisplay component is wrapped with React.memo. React.memo is a way to memorize the whole component. It will rerender only when props change, thus solving our problem completely.

But do not overuse it

While optimizing performance is a noble pursuit, you should always consider the implications and side effects of doing to. In case of React.useMemo there are a few:

  1. The overhead. The hook itself introduces new complex logic, and it might introduce more performance issues than it solves. Do not apply useMemo unless this is a really expensive computation, or, if you are not sure, you can benchmark both ways and make an informed descision.
  2. No guarantees. As per the React docs, you may never depend on the internal mechanisms on useMemo. In other words, while useMemo is supposed to be called only on dependencies change, this is not guaranteed. Your app must still work perfectly well (maybe a bit slow though) if useMemo calls your callback on every render.

Considering React.memo, all these concerns apply as well. But there is one more: React.memo should only be applied to pure components. Another issue has to do with Redux/Context and hooks. Before hooks, the Redux selectors passed store values via props and React.memo would capture them. However, if you are using useSelector/useContext, React.memo will not rerender your component when those change. Because of these complexities, I would advise against React.memo, as useMemo should be sufficient in most cases.

Bonus: useCallback

I noticed that a lot of people are confused with useCallback. useCallback is pretty much the same as useMemo, but for functions. In fact, useCallback(fn, deps) is equivalent to useMemo(() => fn, deps). useCallback is supposed to speed up the app when using a lof of callback functions by not redeclaring them unless the dependencies change. Here is our earlier example which uses useCallback:

Thank you for reading, I hope you enjoyed it. Let me know what you think about useMemo in the comments and check out my other articles:

leave a comment