如docs中所述,useCallback返回一个memoized回调。
传递内联回调和一组输入。 useCallback将返回一个回忆的memoized版本,该版本仅在其中一个输入发生更改时才会更改。将回调传递给依赖于引用相等性的优化子组件以防止不必要的渲染(例如,shouldComponentUpdate)时,这非常有用。
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
但是它如何工作以及在React中最好使用它?
附:我认为使用codepen example进行可视化将有助于每个人更好地理解它。 Explained in docs。
当您希望防止不必要的重新渲染以获得更好的性能时,最好使用此方法。
比较这两种将回调传递给从React Docs获取的子组件的方法:
class Foo extends Component {
handleClick() {
console.log('Click happened');
}
render() {
return <Button onClick={() => this.handleClick()}>Click Me</Button>;
}
}
class Foo extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Click happened');
}
render() {
return <Button onClick={this.handleClick}>Click Me</Button>;
}
}
假设<Button>
被实现为PureComponent
,第一种方式将导致<Button>
每次<Foo>
重新渲染时重新渲染,因为在每个render()
调用中创建了一个新函数。在第二种方式中,handleClick
方法仅在<Foo>
的构造函数中创建一次,并在渲染之间重用。
如果我们使用钩子将两种方法转换为功能组件,那么它们就是等价物(有点):
function Foo() {
const handleClick = () => {
console.log('Click happened');
}
return <Button onClick={handleClick}>Click Me</Button>;
}
function Foo() {
const memoizedHandleClick = useCallback(
() => console.log('Click happened'), [],
); // Tells React to memoize regardless of arguments.
return <Button onClick={memoizedHandleClick}>Click Me</Button>;
}
第一种方法是在每次调用函数组件时创建回调,但在第二种方式中,React会为您记忆回调函数,并且不会多次创建回调。
在大多数情况下,第一种方式做得很好。正如React docs所述:
在渲染方法中使用箭头函数是否可以?一般来说,是的,没关系,并且它通常是将参数传递给回调函数的最简单方法。
如果您确实遇到性能问题,请务必优化!
我做了一个小例子来帮助其他人更好地理解它的行为方式。您可以运行演示here或阅读下面的代码:
import React, { useState, useCallback, useMemo } from 'react';
import { render } from 'react-dom';
const App = () => {
const [state, changeState] = useState({});
const memoizedValue = useMemo(() => Math.random(), []);
const memoizedCallback = useCallback(() => console.log(memoizedValue), []);
const unMemoizedCallback = () => console.log(memoizedValue);
const {prevMemoizedCallback, prevUnMemoizedCallback} = state;
return (
<>
<p>Memoized value: {memoizedValue}</p>
<p>New update {Math.random()}</p>
<p>is prevMemoizedCallback === to memoizedCallback: { String(prevMemoizedCallback === memoizedCallback)}</p>
<p>is prevUnMemoizedCallback === to unMemoizedCallback: { String(prevUnMemoizedCallback === unMemoizedCallback) }</p>
<p><button onClick={memoizedCallback}>memoizedCallback</button></p>
<p><button onClick={unMemoizedCallback}>unMemoizedCallback</button></p>
<p><button onClick={() => changeState({ prevMemoizedCallback: memoizedCallback, prevUnMemoizedCallback: unMemoizedCallback })}>update State</button></p>
</>
);
};
render(<App />, document.getElementById('root'));