这是对 useCallback 和 useMemo 的错误使用吗?
Posted
技术标签:
【中文标题】这是对 useCallback 和 useMemo 的错误使用吗?【英文标题】:Is this incorrect use of useCallback and useMemo? 【发布时间】:2021-03-06 20:35:02 【问题描述】:我们什么时候应该担心函数组件重新定义变量和函数??
我认为第一种情况显然是不必要的,创建函数并不昂贵(并且反应开发工具配置文件测试没有检测到 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>
</>
);
;
另一方面,在另一个示例中,使用 react 开发工具,我们可以检测到 useMemo 将渲染时间缩短了一半:
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>
</>
);
;
但是当我们谈论前端时,仍然不到一毫秒的增益和数组映射和 JSX 数组也不是什么真正昂贵的东西。然而,在每次渲染中运行一个数组映射方法是非常不舒服的。那么,这些事情我们应该怎么做呢? (让我们忘记什么时候我们有 useEffect 依赖于一些使 useMemo 成为必要的变量)
useCallback 和 useMemo 在不需要在每次渲染中重新定义的所有函数和变量中。 useCallback 和 useMemo 仅在我们可以清楚地看到性能提升的情况下使用(即使大多数时间都不能算作对 UX 的改进)。所以我们在第二个例子中需要 useMemo。 useCallback 和 useMemo 仅当在特定情况下不使用它时,我们会得到很大的用户体验下降。所以只有真正昂贵的函数才会使用回调【问题讨论】:
我认为我们应该始终拥有最易读的代码(没有备忘录),直到它成为问题并且您需要优化某些东西 检查reactjs.org/docs/optimizing-performance.html 让我们先编写代码,然后在需要时进行优化。 【参考方案1】:使用它们获得的性能是不同的。是的,对于useMemo
,如果您正在记忆的计算成本很高,它提供了明显的直接好处。但是,对于useCallback
和useMemo
,长期收益是指avoiding unnecessary renders in child components,而不是重新计算成本。
请记住,如果组件中的任何 props 发生变化,它们可能会触发渲染调用,并且在组件树中越靠前,它对性能的影响就越大。这就是我们通常使用这些钩子的原因。
一般来说,组件有几个状态元素,通过备忘录钩子,我们可以建立依赖关系,避免计算冗余。例如,在您的合成案例中,您有两个状态元素:counter
和 list
。每次counter
更改时,您通过使其仅依赖于list
来阻止重新计算domList
。如果这是一个叶节点,那么肯定没有性能提升,但是,这是组件树的根,如果没有这些钩子,性能将不会随着您添加更多状态元素而扩展,您将不得不重构代码以解决性能问题。当然,如果列表包含超过 1000 个元素,<ul>domList</ul>
的成本可能会很高,但 <ul><ExpensiveCutie domList=domList /></ul>
可能需要数周时间才能注意到、调试和重构。
简而言之,我建议在容器组件中使用备忘录挂钩,而不是在叶组件中。然而,如果你的叶子组件在你实现它的时候变成了一个容器呢?
在你走之前,你甚至需要使用 memo() 吗?阅读更多here。
【讨论】:
以上是关于这是对 useCallback 和 useMemo 的错误使用吗?的主要内容,如果未能解决你的问题,请参考以下文章
useMemo和useCallback:何时使用它们,何时不使用它们
useMemo和useCallback:何时使用它们,何时不使用它们
useMemo和useCallback:何时使用它们,何时不使用它们