useCallback和useMemo

时间:2020-02-17 15:55:08

标签: reactjs react-hooks

也许我误会了一些东西,但是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>

4 个答案:

答案 0 :(得分:3)

useMemo用于在组件渲染时运行该函数并返回一个值(假设依赖关系之一已更改)。 useCallback旨在在渲染时返回一个(记忆的)函数,但实际上尚未调用该函数;通常,您只需将此函数传递给onClick参数或类似的东西即可。

如果调用正确,则可以互换使用它们,例如让useMemo返回一个函数等效于useCallback,或者使用useCallback然后调用返回的函数类似于{{ 1}}

答案 1 :(得分:3)

useMemo()使函数仅在输入更改时运行。否则,它返回备忘(缓存)结果。仅建议将useMemo()用于涉及复杂计算(时间复杂度更高)的函数,因为运行useMemo()

useCallback()防止在每次重新渲染时都创建该函数的新实例(我的意思是重新定义了函数),因此,如果我们将子函数作为道具传递给子组件,则可以防止子组件的重新渲染。

答案 2 :(得分:0)

现在(2020年5月25日),useCallbackuseMemo可互换使用:

const fn = () => { function code }

const fn1 = React.useCallback(fn, deps)
const fn2 = React.useMemo(() => fn, deps)

在两种情况下,fn1和fn2都保存在不同的渲染之间。 区别在于useCallback将来可能会有所改进,因为它总是返回相同的函数并将其中继到传递给它的最后一个函数。

我写过here

答案 3 :(得分:0)

在您的示例中,expensiveCalc中的函数useCallback将在每个渲染器上运行,因为您要在每个渲染器的useCallback下方直接调用记忆功能。

useCallback将记住并返回实际功能,因此即使已记住该功能,只要您调用它,它仍将运行。

在该示例中,这实际上是正在发生的事情:

const calcCallback = () => expensiveCalc('useCallback');
const computedCallback = calcCallback();

您不应使用useCallback

如果可能,请将昂贵的函数移至react组件之外,并在外部执行它。 例如:

const calcResult = expensiveCalc('useCallback');
function App() {

如果由于某种原因不可能这样做,那么这里就是useMemo所在的地方。

useMemo将记住从函数返回的值,并在渲染之间保留该值,直到您的依赖项更改为止。

这意味着如果您不想在每个渲染器上运行昂贵的功能而只想要该值,则可以将其移出react组件的范围,或者使用useMemo

有关这两个钩子的更多详细信息,您可以阅读本文的更多信息:What is the difference between useMemo and useCallback?