Ngo*_*Lam 14 javascript typescript reactjs arrow-functions usecallback
假设我们有这样的组件
const Example = () => {
const [counter, setCounter] = useState(0);
const increment = () => setCounter(counter => counter + 1);
return (
<div>
<Button onClick={increment} />
<div>{counter}</div>
</div>
);
}
Run Code Online (Sandbox Code Playgroud)
当我将onClick处理程序作为箭头函数传递时,我eslint抛出一个警告:
error JSX props should not use arrow functions react/jsx-no-bind
Run Code Online (Sandbox Code Playgroud)
正如我从这篇文章的答案中读到的:https : //stackoverflow.com/questions/36677733/why-shouldnt-jsx-props-use-arrow-functions-or-bind# :~: text=Why%20you%20shouldn 't%20use,previous%20function%20is%20garbage%20collected。
简短的回答是因为每次都会重新创建箭头函数,这会损害性能。这篇文章提出的一个解决方案是用空数组包装在useCallback钩子中。当我改成这个时,eslint 警告就真的消失了。
const Example = () => {
const [counter, setCounter] = useState(0);
const increment = useCallback(() => setCounter(counter => counter + 1), []);
return (
<div>
<Button onClick={increment} />
<div>{counter}</div>
</div>
);
}
Run Code Online (Sandbox Code Playgroud)
然而,也有另一种观点认为过度使用useCallback 最终会因为 useCallback 的开销而降低性能。一个例子在这里:https : //kentcdodds.com/blog/usememo-and-usecallback
这真的让我很困惑吗?因此,对于函数式组件,在处理内联函数处理程序时,我应该只编写箭头函数(忽略 eslint)还是始终将其包装在 useCallback 中?
T.J*_*der 26
简短的回答是因为每次都会重新创建箭头函数,这会损害性能。
这是一个普遍的误解。箭头功能重建每次无论哪种方式(虽然useCallback后来者可以立即扔掉)。什么useCallback是使您使用回调的子组件有可能在它被记忆时不重新渲染。
我们先来看看这个误解。考虑useCallback调用:
const increment = useCallback(() => setCounter(counter => counter + 1), []);
Run Code Online (Sandbox Code Playgroud)
是这样执行的:
计算第一个参数() => setCounter(counter => counter + 1),创建一个函数
计算第二个参数[],创建一个数组
打电话useCallback与这两个参数,得到的回复功能
如果您不使用,请与您拥有的进行比较useCallback:
const increment = () => setCounter(counter => counter + 1);
Run Code Online (Sandbox Code Playgroud)
这要简单得多:创建函数。然后它不必执行上面的#2 和#3。
让我们继续讨论什么useCallback是有用的。我们来看看回调的用处:
<Button onClick={increment} />
Run Code Online (Sandbox Code Playgroud)
现在,假设Button与React.memo或相似。如果increment你的组件Button每次渲染都发生变化,那么每次你的组件发生变化时都必须重新渲染;它不能在渲染之间重复使用。但是如果increment在渲染之间是稳定的(因为你使用useCallback了一个空数组),调用的记忆结果Button可以被重用,它不必再次调用。
下面是一个例子:
const increment = useCallback(() => setCounter(counter => counter + 1), []);
Run Code Online (Sandbox Code Playgroud)
const increment = () => setCounter(counter => counter + 1);
Run Code Online (Sandbox Code Playgroud)
请注意,单击中的按钮ComponentA始终会Button再次调用,但单击中的按钮ComponentB不会。
你想什么时候这样做?这主要是取决于你,但它可能是有道理的,当你组件的状态将在不影响内容的方式变化频繁increment,因此不会影响Button 和是否Button有做显著工作渲染时。Button可能不会,但其他子组件可能会。
例如,useCallback如果您将count用作按钮的文本,则在我之前的示例中可能毫无意义,因为这意味着Button无论如何都必须重新渲染:
<Button onClick={increment} />
Run Code Online (Sandbox Code Playgroud)
const { useState, useCallback } = React;
const Button = React.memo(function Button({onClick, children}) {
console.log("Button called");
return <button onClick={onClick}>{children}</button>;
});
function ComponentA() {
console.log("ComponentA called");
const [count, setCount] = useState(0);
// Note: Safe to use the closed-over `count` here if `count `updates are
// triggered by clicks or similar events that definitely render, since
// the `count` that `increment` closes over won't be stale.
const increment = () => setCount(count + 1);
return (
<div>
{count}
<Button onClick={increment}>+</Button>
</div>
);
}
function ComponentB() {
console.log("ComponentB called");
const [count, setCount] = useState(0);
// Note: Can't use `count` in `increment`, need the callback form because
// the `count` the first `increment` closes over *will* be slate after
// the next render
const increment = useCallback(
() => setCount(count => count + 1),
[]
);
return (
<div>
{count}
<Button onClick={increment}>+</Button>
</div>
);
}
ReactDOM.render(
<div>
A:
<ComponentA />
B:
<ComponentB />
</div>,
document.getElementById("root")
);Run Code Online (Sandbox Code Playgroud)
另请注意,这useCallback不是免费的,它会影响回调中的代码。查看示例中ComponentA和ComponentB示例中的回调中的代码。ComponentA(不使用useCallback)可以使用的价值count,它关闭了(限制内!) () => setCount(count + 1)。但是 inComponentB总是必须使用 setter 的回调形式,() => setCount(count => count + 1). 那是因为如果你继续使用increment你创建的第一个,count它关闭的将是陈旧的——你会看到计数变为 1,但永远不会更远。
| 归档时间: |
|
| 查看次数: |
3668 次 |
| 最近记录: |