Are there any benefits in using useMemo
(e.g. for an intensive function call) instead of using a combination of useEffect
and useState
?
Here are two custom hooks that work exactly the same on first sight, besides useMemo
's return value being null
on the first render:
import { expensiveCalculation } from "foo"; function useCalculate(someNumber: number): number { const [result, setResult] = useState<number>(null); useEffect(() => { setResult(expensiveCalculation(someNumber)); }, [someNumber]); return result; }
import { expensiveCalculation } from "foo"; function useCalculateWithMemo(someNumber: number): number { return useMemo(() => { return expensiveCalculation(someNumber); }, [someNumber]); };
Both calculate the result each time their parameter someNumber
changes, where is the memoization of useMemo
kicking in?
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 .
Both useState and useMemo will remember a value across renders. The difference is that: useMemo does not cause a re-render, while useState does. useMemo only runs when its dependencies (if any) have changed, while setSomeState (second array item returned by useState ) does not have such a dependency array.
In real-world useEffect can contain some functionality that we don't want to be repeated if its dependencies do not change. Solution: We can memoize the data object using the useMemo hook so that rendering of the component won't create a new data object and hence useEffect will not call its body.
The useState hook is used for storing variables that are part of your application's state and will change as the user interacts with your website. The useEffect hook allows components to react to lifecycle events such as mounting to the DOM, re-rendering, and unmounting.
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.
Suppose we have:
function expensiveCalculation(x) { return x + 1; }; // Maybe I'm running this on a literal potato
Lets suppose someNumber
is initially 0:
useMemo
version immediately renders 1
.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:
useMemo
runs and 3
is rendered.useEffect
version runs, and renders 1
again, then the effect triggers and the component reruns with the correct value of 3
.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, IMO. It doesn't introduce unnecessary mutable state and has fewer moving parts.
So you're better off just using useMemo
here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With