Reputation: 81
When should we worry about functional components redefining variables and functions??
I think this first case is clearly unnecessary, functions is not expensive to create(and the react dev tools profile test didn't detect performace change with useCallback)
import React, { useState, useMemo, useCallback } from "react";
export const App = () => {
const [counter, setCounter] = useState(1);
const showCounter = useCallback(() => {
console.log(counter);
}, [counter]);
return (
<>
<button onClick={showCounter}>Show</button>
<button onClick={() => setCounter(counter + 1)}>Add</button>
</>
);
};
In the other hand, in this other exemple, with react dev tools we can detect that useMemo cuts in half the time of rendering:
import React, { useState, useMemo, useCallback } from "react";
export const App = () => {
const [counter, setCounter] = useState(1);
const [list, setList] = useState<number[]>([]);
function setListItem(item: number) {
setList([...list, item]);
}
//should we useMemo here?
const domList = useMemo(
() =>
list.map((value: number, index: number) => (
<li key={index}> {value} </li>
)),
[list]
);
return (
<>
<p>{counter}</p>
<button onClick={() => setCounter(counter - 1)}>Subtract</button>
<button onClick={() => setCounter(counter + 1)}>Add</button>
<button onClick={() => setListItem(counter)}>Push to array</button>
<h1>List</h1>
<ul>{domList}</ul>
</>
);
};
But still less than a millisecond of gain and array maps and JSX array are not something really expensive when we talk about front-end either. However, leaving an array map method run in each render is very uncomfortable to see. So, what of those things should we do? (Let's forget about when we have useEffect that depends of some variables that make the useMemo necessary)
Upvotes: 1
Views: 762
Reputation: 2693
The performance you gain with each of them is different. Yes, for useMemo
if the calculation you are memoizing is expensive, it offers a clear immediate benefit. However, for useCallback
and useMemo
, the long-term benefits refer to avoiding unnecessary renders in child components, not the recalculation cost.
Remember, if any of the props in a component change, they may trigger a render call, and the higher in the component tree this is the case, the more it will affect performance. That is the reason we normally use those hooks.
In general, components have several state elements, and with memo hooks, we can establish dependencies to avoid computational redundancy. For instance, in your synthetic case, you have two state elements: counter
, and list
. Each time counter
changes, you prevented domList
to be recalculated by making it dependent only on list
. If this was a leaf node, sure no performance gain, however, this is the root of your component tree, and without those hooks, performance will not scale as you add more state elements and you will have to refactor your code to address performance. Sure, <ul>{domList}</ul>
can be costly if the list has more than 1000 elements, but <ul><ExpensiveCutie domList={domList} /></ul>
can take you weeks to notice, debug, and refactor.
In short, I recommend using memo hooks in container components and not in leaf components. Yet, what if your leaf component becomes a container as you are implementing it?
Before you go, do you even need to use memo()? Read more here.
Upvotes: 1