В чем разница между useCallBack
и useMemo
? И почему useMemo
и useCallback
ожидают функции? Если вы работали с React Hooks, возможно, вы задавали себе эти вопросы.
Мы рассмотрим, чем они отличаются друг от друга.
В чем разница между useCallback и useMemo? 🤔
Еще одно примечание. В этой статье предполагается базовое понимание хуков. Вы должны были прочитать Крючки с первого взгляда.💌
Абстрактный
Документы React говорят, что useCallback:
Возвращает запомненный обратный вызов.
И это useMemo:
Возвращает запомненное значение.
Другими словами, useCallback
дает вам ссылочное равенство между рендерами для функций. И useMemo
дает вам ссылочное равенство между рендерингами для значений.
useCallback
и useMemo
ожидают функцию и массив зависимостей. Разница в том, что useCallback
возвращает свою функцию при изменении зависимостей, а useMemo
вызывает свою функцию и возвращает результат.
Поскольку в JavaScript есть первоклассные функции, useCallback(fn, deps)
эквивалентно useMemo(() => fn, deps)
.
Перевод
Давайте разберемся и объясним приведенное выше абстрактное описание на простых примерах. Я расскажу обо всем, но не стесняйтесь пропустить то, что вы уже знаете.
Первоклассные функции
Мы говорили, что в JavaScript функции первоклассны. Это означает, что в JavaScript вы можете присвоить функцию значению.
Хранение функций в переменных позволяет использовать их как (cursive точки важны для React Hooks):
- аргументы для других функций,
- возвращать значения из функций,
- значения для ключей объекта,
- значения в массиве,
- даже как ключи к Карте объекта.
Функции, которые возвращают функции или принимают функции в качестве входных данных, называются Функциями высшего порядка.
Ссылочное равенство
Если вы используете JavaScript, вы, вероятно, знаете, что существует два оператора сравнения равенства. ==
для абстрактного равенства и ===
для строгого равенства. Равенство JavaScript может быть странным, и на эту тему уже написано много отличных статей. Прочтите их для более подробного ознакомления, так как я рассмотрю только основные случаи, относящиеся к useCallback
и useMemo
.
Ссылочное равенство использует строгое сравнение на равенство, которое истинно, если операнды одного типа и содержимое совпадает.
Обратите внимание, как foo === sameFoo
возвращает false
(в отличие от greeting === otherGreeting
). Это потому что
- два разных объекта никогда не равны ни для строгого, ни для абстрактного сравнения,
- выражение, сравнивающее объекты, истинно только в том случае, если операнды ссылаются на один и тот же объект,
- а функции — это объекты в JavaScript.
foo
и sameFoo
имеют одинаковое определение, но ссылаются на два разных объекта.
API
API useCallback
и useMemo
выглядят одинаково. Они оба принимают функцию и массив зависимостей.
useCallback(fn, deps); useMemo(fn, deps);
Так в чем же разница? useCallback
возвращает свою функцию без вызова, поэтому вы можете вызвать ее позже, а useMemo
вызывает свою функцию и возвращает результат.
Примечание. Теперь — зная о функциях первого класса — вы должны понять, почему useCallback(fn, deps)
эквивалентно useMemo(() => fn, deps)
.
В реальном мире приведенные выше примеры бессмысленны. Я просто дал его, чтобы помочь вам понять API. Если вы можете передать функцию useCallback
или useMemo
непосредственно и можете использовать эту функцию с пустыми зависимостями, вы можете определить функцию вне компонента (и обойтись без useCallback
или useMemo
). Поэтому в реальном мире вы увидите нечто подобное.
useCallback
обычно принимает встроенный обратный вызов, который вызывает функцию, использующую зависимости. И useMemo
принимает функцию «создания», которая вызывает некоторую функцию и возвращает ее результаты.
Еще одно примечание: следующее может показаться тривиальным для вас, но мне пришлось долго думать об этом, чтобы получить его. Следующий код неверен. Вы не можете использовать useCallback
для запоминания значений. Значение useCallback(fn(), deps)
не имеет значения.
При каждом изменении name
sum
пересчитывается. useCallback
и useMemo
очень полезны, потому что допускают ленивую оценку. result
с нетерпением оценивается при каждом рендеринге. Попробуйте и посмотрите на свою консоль.
Итак, зачем нам два хука, предназначенных для запоминания значений?
Применение
Вы хотите использовать useCallback
или useMemo
всякий раз, когда зависите от ссылочного равенства между рендерингами. Я обнаружил, что в основном использую его для useEffect
, React.memo
и useMemo
, чтобы заменить shouldComponentUpdate
с React.PureComponent
, потому что зависимости этих хуков проверяются на ссылочное равенство.
Допустим, у вас есть компонент, который с учетом userId
отображает данные пользователя.
Можете ли вы найти ошибку (ы)?
Если у вас установлен eslint-plugin-react-hooks
, он будет кричать на вас за то, что вы исключили fetchUser
из зависимостей useEffect
. Но бездумное его добавление на самом деле создаст бесконечный цикл!
В React функции, определенные в функциональных компонентах, пересоздаются при каждом рендеринге из-за замыкания, что приводит к ссылочному неравенству.
Есть два способа решить эту проблему. По общему признанию, самым элегантным было бы переместить fetchUser
внутрь useEffect
и добавить userId
в качестве зависимости.
Но это статья о useCallback
и useMemo
, так что давайте решим задачу с помощью первого. (И, возможно, вы хотели бы использовать fetchUser
function в нескольких местах.)
Вы можете определить fetchUser
с помощью useCallback
, чтобы функция оставалась неизменной, пока не изменится userId
.
И, наконец, допустим, вы фильтруете всех пользователей и отображаете их в списке.
Это неэффективно. Мы можем использовать useMemo
только для пересчета отфильтрованных пользователей при изменении запроса.
Краткое содержание
- С помощью
useCallback
вы можете определить функцию, которая имеет ссылочное равенство между рендерингами. - Вы можете использовать
useMemo
для вычисления значения, которое имеет ссылочное равенство между рендерингами.
Оба изменяют свое возвращаемое значение только при изменении их зависимостей.
Если вам понравилась эта статья, возможно, вы захотите аплодировать, потому что она мне очень поможет. Спасибо! 🤗
Большая заслуга принадлежит Ян Хестерс!! для этого поста.