也许我误会了一些东西,但是useCallback Hook每次重新渲染时都会运行。
我传递了输入-作为useCallback的第二个参数-不可更改的常量-但返回的备注回调在每次渲染时仍运行我的昂贵计算(我很确定- 您可以在下面的代码段中自行检查)。
我已经将useCallback更改为useMemo,并且useMemo可以按预期工作—在传递的输入更改时运行。并真正记住了昂贵的计算。
'use strict'; const { useState, useCallback, useMemo } = React; const neverChange = 'I never change'; const oneSecond = 1000; function App() { const [second, setSecond] = useState(0); // This 👇 expensive function executes everytime when render happens: const calcCallback = useCallback(() => expensiveCalc('useCallback'), [neverChange]); const computedCallback = calcCallback(); // This 👇 executes once const computedMemo = useMemo(() => expensiveCalc('useMemo'), [neverChange]); setTimeout(() => setSecond(second + 1), oneSecond); return ` useCallback: ${computedCallback} times | useMemo: ${computedMemo} | App lifetime: ${second}sec. `; } const tenThousand = 10 * 1000; let expensiveCalcExecutedTimes = { 'useCallback': 0, 'useMemo': 0 }; function expensiveCalc(hook) { let i = 0; while (i < tenThousand) i++; return ++expensiveCalcExecutedTimes[hook]; } ReactDOM.render( React.createElement(App), document.querySelector('#app') ); <h1>useCallback vs useMemo:</h1> <div id="app">Loading...</div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
TL; DR;
useMemo
useCallback
useRef
useState
长版:
useMemo 着重避免繁重的计算。
useCallback着重于另一件事:修复内联事件处理程序(如onClick={() => { doSomething(...); }引起PureComponent子项重新呈现)时的性能问题(因为函数表达式每次都有参照性的不同)
onClick={() => { doSomething(...); }
PureComponent
也就是说,useCallback它更接近useRef,而不是一种记忆计算结果的方法。
查看文档,我确实同意它看起来令人困惑。
useCallback会返回一个已记忆的回调版本,仅当输入之一发生更改时才会更改。 当将回调传递给依赖于引用相等性的优化子组件以防止不必要的渲染 (例如,shouldComponentUpdate) 时, 此方法很有用。
例
假设我们有一个PureComponent基于子对象的子对象<Pure />,该子对象只有props在更改后才会重新渲染。
<Pure />
props
每次重新渲染父级时,此代码都会重新渲染子级,因为内联函数每次都在引用方面有所不同:
function Parent({ ... }) { const [a, setA] = useState(0); ... return ( ... <Pure onChange={() => { doSomething(a); }} /> ); }
我们可以借助useCallback:
function Parent({ ... }) { const [a, setA] = useState(0); const onPureChange = useCallback(() => {doSomething(a);}, []); ... return ( ... <Pure onChange={onPureChange} /> ); }
但是一旦a更改,我们发现onPureChange我们创建的处理程序函数以及为我们记住的React仍然指向旧a值!我们有一个错误而不是性能问题!这是因为onPureChange使用闭包来访问a变量,该变量是在onPureChange声明时捕获的。为了解决这个问题,我们需要让React知道在哪里删除onPureChange并重新创建/记住(记忆)指向正确数据的新版本。为此,我们在useCallback的第二个参数中添加a`了一个 依赖 项:
a
onPureChange
useCallback的第二个参数中添加
const [a, setA] = useState(0); const onPureChange = useCallback(() => {doSomething(a);}, [a]);
现在,如果a已更改,React将重新渲染组件。在重新渲染期间,它会发现的依赖关系onPureChange有所不同,因此需要重新创建/存储新版本的回调。终于一切正常!