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,
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
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.
useMemo and when should you use it, check out this example. Firstly, look at this code without
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
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 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:
- 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.
- No guarantees. As per the React docs, you may never depend on the internal mechanisms on
useMemo. In other words, while
useMemois 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
useMemocalls 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
React.memo will not rerender your component when those change. Because of these complexities, I would advise against
useMemo should be sufficient in most cases.
I noticed that a lot of people are confused with
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
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: