admin管理员组文章数量:1134599
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:
useEffect & useState
import { expensiveCalculation } from "foo";
function useCalculate(someNumber: number): number | null {
const [result, setResult] = useState<number | null>(null);
useEffect(() => {
setResult(expensiveCalculation(someNumber));
}, [someNumber]);
return result;
}
useMemo
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?
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:
useEffect & useState
import { expensiveCalculation } from "foo";
function useCalculate(someNumber: number): number | null {
const [result, setResult] = useState<number | null>(null);
useEffect(() => {
setResult(expensiveCalculation(someNumber));
}, [someNumber]);
return result;
}
useMemo
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?
4 Answers
Reset to default 232The 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:
// Maybe I'm running this on a literal potato
function expensiveCalculation(x) { return x + 1; };
Lets suppose x
is initially 0:
- The
useMemo
version immediately renders1
. - The
useEffect
version rendersnull
, then after the component renders the effect runs, changes the state, and queues up a new render with1
.
Then if we change x
to 2:
- The
useMemo
runs and3
is rendered. - The
useEffect
version runs, and renders1
again, then the effect triggers and the component reruns with the correct value of3
.
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.
I think there are two main points you should consider when choosing between them.
- 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.
- 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.
I would say other than the async nature, there might be some difference in terms how they are designed.
useEffect
is a collective call, async or not, it's collected after all components are rendered.
useMemo
is a local call, which has only something to do with this component. You could just think of useMemo
as another assignment statement with benefits to use the assignment from last update.
This means, useMemo
is more urgent, and then useLayoutEffect
and the last being useEffect
.
useState
+ useEffect
is not necessarily a good pattern.
const [state, setState] = useState(initialValue)
provides you with setState
to explicitly change the state.
In your example, call setState
where some change to someNumber
is made instead of using useEffect
.
I know, this is not always possible.
E.g. someNumber
might belong to some outer context and changes are not made by the parent component but by siblings or cousins.
In case it's a child component that causes a change to someNumber
, passing down setState
to the child as prop isn't the worst idea.
Before slapping useEffect
on useState
, consider changes to your component tree. For an expensive calculation within some component, you can use simple Javascript laziness to make sure, the calculation won't happen on every re-render:
const [state, setState] = useState(() => { return /* expensive calculation */ })
^^^^^ lazy
As pointed out in a comment by @mark-adamson, when you want to display some component before the expensive calculation (e.g. with a spinner), you need useState
, e.g. with a nullable state, because the absence of the result of the calculation is a legitimate state and needs some representation.
useEffect
is still optional even in this case.
本文标签: javascriptuseMemo vs useEffectuseStateStack Overflow
版权声明:本文标题:javascript - useMemo vs. useEffect + useState - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736798120a1953368.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
null
on the first render, while the second won't? – Jonas Wilms Commented May 7, 2019 at 19:08