UseMemo vs. useEffect + useState

Photo by Andrew Neel on Unsplash

UseMemo vs. useEffect + useState

React: useEffect vs useMemo vs useState, which and when use them?

If you are learning hooks probably this question hasn't come across to you yet.

On the other hand I have seen even experienced developers and software engineers not aware of which hook use especially when it came with those that are quite similar and then they end up write always the same function / hooks because it seemed to them the other solutions are "useless"

But if you are more experience you know that for the right job you need the right tool, and thus applies also to use the right hook for the right use cases.

But let's start with code, that I will also recall in a sandbox for more details or for runtime examples.

For example let's consider a simple counter:

useEffect & useState

// we need also the useState hook in order to store the state of 
// the counter variable(result) and increment it with the 
// asyncronous function of the hook destructuring ( setResult )

import { expensiveCalculation } from "foo"; 

function useCalculate(someNumber: number): number {
  const [result, setResult] = useState<number>(null);

  useEffect(() => {
    setResult(expensiveCalculation(someNumber));
  }, [someNumber]);

  return result;
}

useMemo

import { expensiveCalculation } from "foo";

function useCalculateWithMemo(someNumber: number): number {
    return useMemo(() => {
        return expensiveCalculation(someNumber);
    }, [someNumber]);
};

Here the sandox to see it live.

They both calculate the count but if you have run the sandbox you can notice that the case with useEffect has a little text showing for few millisecond and the disappearing.

This is because Effect( let's call it in a kind of short way) does run just after the DOM is rendered by React, instead Memo its run immediately does in this case is way much faster.

Well as you may guess the answer is not, though I have seen some PR where engineers where basically using it everywhere.

Some analysis

The useEffect and setState will cause extra renders on every change: the first render will "lag behind" with stale data and then it'll immediately queue up an additional render with the new data.

Lets suppose someNumber is initially 0:

The useMemo version immediately renders 1.

The useEffect version renders null, then after the component renders the effect runs, changes the state, and queues up a new render with 1. Then if we change someNumber to 2:

The useMemo runs :

  • 3 is rendered.

The useEffect version runs,

  • renders 1 again, then the effect triggers and the component reruns with the correct value of 3.

you should consider useMemo as pure optimization technique. Your program should continue to work correctly even if you replace useMemo with regular function call.

Recap

1 . Time when function called.

useEffect called after component has been rendered, so you can access DOM from it. For example, this is important if you want to access DOM elements via refs.

2. Semantic guarantees.

useEffect guarantees that it will not be fired if dependencies have not changed. useMemo does not give such guarantees.

As stated in the React documentation, you should consider useMemo as pure optimization technique. Your program should continue to work correctly even if you replace useMemo with regular function call.

useEffect + useState can be used to control updates. Even to break-up circular dependencies and prevent infinite update loops.

Also, why is not recommended to use always useMemo is that as the React API docs mention, useMemo doesn’t guarantee that the memoized function won’t be executed again if the dependencies don’t change because React may, in the future, discard cache to improve performance. So if the memoized function has side effects of some kind, it might be smarter to use a custom hook.

In terms of how often expensiveCalculation runs, the two have identical behavior, but the useEffect version is causing twice as much rendering which is bad for performance for other reasons.

Plus, the useMemo version is just cleaner and more readable, in this specific case.

It doesn't introduce unnecessary mutable state(this is the advantage that of course is a downside when you need to mutate state properly) and has fewer moving parts.

So you're better off just using useMemo here.

Should we always use useMemo() and take advantage of its memoization capability?

Let's try to answer, as stated in the React docs

In general one should avoid to useMemo() when side effects are involved. At this point one is tempted to think that is an easy thing, but I can guarantee that in real case, is not, for example, what about retrieve some APIs, aren't they a side effect, and what use to handle them especially if they could take long and are update on regular basis, you may read Asynchronous calls in React to get a deep answer but in general, useMemo() is not at all the best option to handle them and anything that happens outside the component where it used, as indeed everything outside the Component is a side effect.

Practical examples( coming...)