Вы вызываете запомненный обратный вызов каждый раз, когда делаете:
const calcCallback = useCallback(() => expensiveCalc('useCallback'), [neverChange]);
const computedCallback = calcCallback();
Вот почему счет useCallback
увеличивается.Однако функция никогда не меняется, она никогда не создает ***** новый обратный вызов, он всегда один и тот же.Значение useCallback
правильно выполняет свою работу.
Давайте внесем некоторые изменения в ваш код, чтобы убедиться, что это правда.Давайте создадим глобальную переменную lastComputedCallback
, которая будет отслеживать, будет ли возвращена новая (другая) функция.Если возвращается новая функция, это означает, что useCallback
просто «выполняется снова».Поэтому, когда он снова запустится, мы назовем expensiveCalc('useCallback')
, так как вы рассчитываете, сработал ли useCallback
.Я делаю это в приведенном ниже коде, и теперь ясно, что useCallback
запоминает, как и ожидалось.
Если вы хотите, чтобы useCallback
каждый раз заново создавал функцию, то раскомментируйте строку в массивекоторый проходит second
.Вы увидите, как он воссоздает функцию.
'use strict';
const { useState, useCallback, useMemo } = React;
const neverChange = 'I never change';
const oneSecond = 1000;
let lastComputedCallback;
function App() {
const [second, setSecond] = useState(0);
// This ? is not expensive, and it will execute every render, this is fine, creating a function every render is about as cheap as setting a variable to true every render.
const computedCallback = useCallback(() => expensiveCalc('useCallback'), [
neverChange,
// second // uncomment this to make it return a new callback every second
]);
if (computedCallback !== lastComputedCallback) {
lastComputedCallback = computedCallback
// This ? executes everytime computedCallback is changed. Running this callback is expensive, that is true.
computedCallback();
}
// This ? executes once
const computedMemo = useMemo(() => expensiveCalc('useMemo'), [neverChange]);
setTimeout(() => setSecond(second + 1), oneSecond);
return `
useCallback: ${expensiveCalcExecutedTimes.useCallback} 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 < 10000) 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>
Преимущество useCallback
заключается в том, что возвращаемая функция одинакова, поэтому реакция не removeEventListener
и addEventListener
на элемент каждый раз, ЕСЛИ computedCallback
не изменится.И computedCallback
изменяется только при изменении переменных.Таким образом реагировать будет только addEventListener
один раз.
Отличный вопрос, я многому научился, ответив на него.