В чем разница между 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). Это потому что

  1. два разных объекта никогда не равны ни для строгого, ни для абстрактного сравнения,
  2. выражение, сравнивающее объекты, истинно только в том случае, если операнды ссылаются на один и тот же объект,
  3. а функции — это объекты в 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, так что давайте решим задачу с помощью первого. (И, возможно, вы хотели бы использовать fetchUserfunction в нескольких местах.)

Вы можете определить fetchUser с помощью useCallback, чтобы функция оставалась неизменной, пока не изменится userId.

И, наконец, допустим, вы фильтруете всех пользователей и отображаете их в списке.

Это неэффективно. Мы можем использовать useMemo только для пересчета отфильтрованных пользователей при изменении запроса.

Краткое содержание

  • С помощью useCallback вы можете определить функцию, которая имеет ссылочное равенство между рендерингами.
  • Вы можете использовать useMemo для вычисления значения, которое имеет ссылочное равенство между рендерингами.

Оба изменяют свое возвращаемое значение только при изменении их зависимостей.

Если вам понравилась эта статья, возможно, вы захотите аплодировать, потому что она мне очень поможет. Спасибо! 🤗
Большая заслуга принадлежит Ян Хестерс!! для этого поста.