1

When Using useMemo is A Really Bad Idea

 2 years ago
source link: https://blog.bitsrc.io/when-using-usememo-is-a-really-bad-idea-a2bdeb909812
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

When Using useMemo is A Really Bad Idea

useMemo without a doubt is a handy utility that can improve an application’s performance. However, in my daily work I have noticed that some engineers tend to use it in every possible situation so I set out on a mission to investigate when using useMemo is not such a good idea. And here are my findings.

Before I dive into when not to use useMemo, here is a very quick reminder of what useMemo is and how it is usually used.

useMemo is a built-in Hook in React. It takes a function as a parameter and returns a memoized return value of that function. It might be necessary when “expensive” operations need to be performed (for example, fetching data). Let’s say, we want to call a function to fetch a list of movies (stored as an object), based on a genre selected by the user and we pass its return value as a prop to a child component.

const MovieListParentComponent({ genre }) { const listOfMovies = fetchListOfMovies(genre); return <MovieListChildComponent listOfMovies={listOfMovies}/>}

Because objects are stored by reference, without useMemo, <MovieListChildComponent/> would be re-rendered every time its parent component is re-rendered. This happens because fetchListOfMovies would be called on every new render and the reference to listOfMovies would change, even if its value remains the same.

But what if we only need to refetch that data when specific prop changes? In our case, it would happen when genre prop is updated. This is where useMemo comes into play. The hook takes fetchListOfMovies function as a parameter and an array of dependencies. So we could “listen” to change in genre by setting it as a dependency and the function that we pass to useMemo would be run only when the prop has changed. Otherwise data would not be re-fetched and <MovieListChildComponent /> would not be re-rendered no matter how many times we re-render the parent component.

const MovieListParentComponent({genre}) { const memoizedListOfMovies = useMemo(() => fetchListOfMovies(genre), [genre]); return <MovieListChildComponent listOfMovies={memoizedListOfMovies}/>}

So far it looks really exciting! So why not use it?

First of all, we cannot completely trust useMemo to keep reference to our data 100% of the time. In some cases useMemo might not retain our data in order to save memory so if we generate some data with the function passed to useMemo and then rely on it for the application to work, unexpected bugs can happen due to missing data. According to React documentation, useMemo should be used to optimize performance, not to store data.

In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.

Another and the most obvious situation in which useMemo probably does more harm than good is when the function being memoized does not perform expensive operations. useMemo itself requires memory so if we are trying to over-optimize by memoizing every function, it might slow the application down.

We should also not use useMemo when the function returns a primitive value, such as a boolean or a string. Because primitive values are passed by value, not by reference, it means that they always remain the same, even if the component is re-rendered.

const BigMovieComponent({movieId}) { const movieTitleAsString = getMovieTitle(movieId); return <ListChildComponent movieTitle={movieTitleAsString}/>}

In this case, movieTitleAsString returns a primitive value, which is passed as a prop to our child component. If the memoized value is the same, the child component will not be re-rendered. However, it is worth noting that the function, which is passed to useMemo will still run when the parent component is rendered and if expensive computations are being made, it will impact performance so here it is up to the engineer to decide if the operation is complex enough to be memoized. In most of the cases, saving memory when computing a primitive value does not outweigh the memory cost of using useMemo.

It is also not necessary to memoize JavaScript array and object methods, such as map, forEach, hasProperty, etc., if the operation is not expensive (for example, low Big O complexity or a small number of elements), because they have already been optimized by JavaScript itself.

Another type of calculation we should not memoize is the initial data in useState. Even if data stored in useState was complex and used a lot of memory, because of how useState works, it will not be recalculated if the component is not unmounted. (Unless, of course, we deliberately update the state)

Back to React documentation:

The initialState argument is the state used during the initial render. In subsequent renders, it is disregarded.

And this is it! Knowing a few use cases where useMemo does not really make sense can save you some time and decision-making headache. But aside from a couple of quite easy- to-remember situations , memoize it away!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK