useCallback: Руководство, варианты использования, примеры

Это легко читаемое руководство, в котором мы рассмотрим

  • Что такое Реакт useCallback?
  • Когда использовать React useCallback? (случаи использования)
  • Реальные примеры useCallback
  • Советы по устранению неполадок

Что такое React, используйте обратный вызов

useCallback — это React Hook. (Хуки — это функции JS, которые позволяют добавлять к вашему компоненту дополнительные встроенные функции)

useCallback — это перехватчик, который позволяет кэшировать определение функции между рендерингами.

useCallback не вызывает функцию и не кэширует возвращаемое значение. Он кэширует само определение функции.

Справочник по использованию CallBack

useCallback(function, [dependencies]);

Поскольку useCallback — это хук реагирования, вам нужно вызвать его на верхнем уровне вашего компонента.

import { useCallback } from 'react';
export default function ReactCommentsSection({ postId, username }) {
  const handleCommentSubmission = useCallback((Text) => {
    post('/post/' + postId + '/comment', {
      username,
      comment: commentText,
    });
  }, [postId, username]);
}

Параметры

fn: (функция для запоминания)

Это функция, которую вы хотите кэшировать.

Эта функция может принимать любые аргументы и возвращать любые данные.

React вернет это определение функции (но не вызовет ее) во время первого рендеринга.

На следующих рендерах:

  • Если dependencies не изменены, реакция даст вам ту же кэшированную функцию.
  • В противном случае, если зависимости изменились, реакция даст вам текущую функцию, которую вы ей передали. Это если зависимости изменились.

Зависимости

Массив реактивных значений, от которых зависит функция или на которые ссылается fn.

Реактивные значения — это реквизиты, состояние и все переменные/функции, которые находятся внутри вашего компонента.

Реакция выполнит неглубокую проверку с помощью метода Object.is, чтобы проверить, изменились ли зависимости.

Вы можете использовать линтер кода, чтобы проверить, все ли зависимости перечислены в массиве зависимостей.

Список зависимостей должен оставаться постоянным и будет записан в строке в массиве типа [dep1, dep2, dep3].

Для чего используется обратный вызов Return

  • начальный рендеринг: при первоначальном рендеринге крючок useCallback возвращает функцию, которую вы ему передали.
  • Последующие рендеры:
  • Если зависимости не изменились, она вернет кешированную функцию.
  • Если зависимости изменились, он вернет новую функцию, то есть функцию, которую вы передали во время этого рендеринга.

Предостережение

Всегда объявляйте useCallback вверху вашего компонента, потому что это крючок.

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

Управление кэшем

  • React не будет удалять кешированную функцию, если для этого нет веской причины.
  • например: во время разработки реакция будет через кешированную функцию, если вы отредактируете компонент
  • Если компонент приостанавливает работу на начальном этапе монтажа, реакция отбрасывает кэшированные данные.

Примеры и варианты использования

Вариант использования: пропуск повторного рендеринга компонентов для оптимизации производительности.

1. ненужные повторные рендеры

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

Из-за ограничений ресурсов логика или масштаб сверления компонентов и опор могут привести к неоптимальной работе пользовательского интерфейса.

Кэширование значений или функции

Одним из методов оптимизации производительности является кэширование значений, которые требуют большого количества вычислений и не сильно меняются от одного повторного рендеринга к другому.

вы можете использовать useMemo для кэширования значений, но как насчет функций?

Поскольку обозначение {} создает новый объект, обозначение функции, например function () {} или () => {}, создает новую функцию.

Обычно это не проблема, но создание новой функции при каждом рендеринге противоречит цели кэширования.

Итак, нам нужно кэшировать функции, которые мы передаем в качестве реквизита, здесь useCallback пригодится

Пример 1: Раздел комментариев

Рассмотрим раздел комментариев на веб-сайте. Здесь у нас есть CommentSection Компонент

import { useCallback } from 'react';
export default function CommentSection({ userName, userToken, postId, }) {
  const handleCommentSubmission = useCallback((textOfTheComment) => {
    post('/post/' + postId + '/comment', {
      username,
      comment: textOfTheComment,
      token: userToken,
    });
  }, [postId, userName, userToken]);
}

Здесь у нас есть компонент CommentSection. Наш компонент принимает три реквизита: 1. userName, userToken и postId.

Затем у нас есть функция handleCommentSection с хуком useCallback, который проверяет, изменилась ли какая-либо из зависимостей в массиве зависимостей.

зависимости: [postId, userName, userToken]

Если зависимости не изменились, то useCallback возвращает кэшированную функцию, в противном случае useCallback возвращает новую функцию.

Когда использовать useCallback или useMemo?

И useCallback, и useMemo используются для кэширования чего-либо для оптимизации производительности.

useMemo используется для кэширования значений, а useCallback — для кэширования определений функций.

Итак, чтобы предотвратить ненужные повторные рендеринги

Разница между useMemo и useCallback

Хук useMemo принимает функцию и массив зависимостей в качестве аргументов.

useMemo вызывает функцию и кэширует возвращаемое ею значение. Затем функция вызывается только при изменении какой-либо из зависимостей.

const ComponentCompute = useMemo(() => {
  return calculateReq(product);
}, [product]);

здесь возвращаемое значение calculateReq будет кэшироваться и будет пересчитываться только при изменении product

useCallback также принимает функцию и массив зависимостей, но вместо кэширования возвращаемого значения функции он кэширует само объявление функции.

useMemo

const requirements = useMemo(() => { // Calls your function and caches its result   
 return computeRequirements(product);  }, [product]);

useCallback

import { useCallback } from 'react';
export default function CommentSection({ userName, userToken, postId, }) {
  const handleCommentSubmission = useCallback((textOfTheComment) => {
    post('/post/' + postId + '/comment', {
      username,
      comment: textOfTheComment,
      token: userToken,
    });
  }, [postId, userName, userToken]);
}

Когда вызывается функция handleCommentSubmission, useMemo вызывает кэшированную версию, и она остается неизменной при любом рендеринге, за исключением случаев изменения одной из зависимостей.

Если вы знакомы с useMemo, вы можете рассматривать useCallback как оболочку useMemo со встроенной функциональностью.

вы можете использовать useMemo вместо useCallback, добавив такую ​​функцию обратного вызова

function useCallback(fn, dependencies) {
  return useMemo(() => fn, dependencies);
}

Когда использовать useCallback

Это зависит от типа вашего приложения или веб-сайта. Если у вас есть приложение, которое больше похоже на веб-сайт, то есть вы сразу меняете большие части или свой пользовательский интерфейс, то запоминание кэширования не так уж полезно.

И если вам нужно изменить только небольшие части вашего общего пользовательского интерфейса, например, в приложении для рисования или каком-либо другом приложении

Если вы загружаете сразу много пользовательского интерфейса, а затем вам нужно внести в него небольшие изменения, тогда кэширование будет хорошей стратегией.

Вот несколько случаев, когда использование Callback является хорошей идеей.

  1. Если вы используете useMemo в компоненте и хотите передать функцию этому компоненту в качестве реквизита.
  2. Если вы используете функцию внутри других React Hooks. Для example от нее зависит другая функция, заключенная в useCallback, вы зависите от этой функции из useEffect

Примечание. useCallback не препятствует созданию функции. функция всегда создается, и это хорошо, React просто игнорирует созданную функцию и вместо этого предоставляет вам кешированную версию

Сделайте запоминание и кэширование ненужными, следуя нескольким принципам:

  1. Визуально оберните компонент

Когда компонент визуально обертывает другие компоненты, например, большой компонент веб-страницы, содержащий более мелкие компоненты пользовательского интерфейса, такие как кнопки и т. д.

Пусть он примет JSX в качестве своих дочерних элементов. вот так,

<div>
  <img />
    <button />
</div>

Затем, если обновления компонентов оболочки отреагируют, не обновляйте дочерние компоненты.

2. Местный штат

Всегда отдавайте предпочтение местному штату и не поднимайте его. Поднимайте состояние только тогда, когда это необходимо.

Это важно для кратковременных данных, например, когда пользователь наводит курсор на кнопку или есть небольшая форма.

3. Сохранение чистоты логики рендеринга

Компонент всегда должен быть чистым. Это означает, что для данного входного реквизита вывод всегда должен оставаться неизменным.

или повторный рендеринг компонента никогда не должен давать разные результаты

Исправьте ошибку вместо кэширования компонента

4. Избегайте ненужных эффектов, обновляющих состояние

Эффекты — это обновления, которые приложение запрашивает выполнить после завершения обновления пользовательского интерфейса. Однако если обновлений много, приложение работает по кругу и вызывает проблемы с производительностью.

следовательно, используйте обновления только при необходимости

5. Удаление ненужных зависимостей из эффектов

вместо кэширования значений и объявления функции вы можете попытаться уменьшить зависимости, например удалить некоторые объекты или функции.

ЕСЛИ взаимодействие по-прежнему тормозит, воспользуйтесь профилировщиком инструментов разработки React, чтобы выяснить, какие компоненты могут извлечь выгоду из запоминания.

Разница между useCallback и прямым объявлением функции

  1. Непосредственное объявление функции

Непосредственное объявление функции внутри компонента создает новый экземпляр функции каждый раз при повторной визуализации компонента. Это связано с тем, что, поскольку {} создает новый объект, новое функциональное объявление, такое как function (){} или ()=>{}, создает новую функцию.

function SomeComponent(props) {
  function handleClick() {
    console.log('Somebody clicked the button');
  }
  return <button onClick={handleClick}>Click the button        </button>;
}

Здесь каждый раз, когда SomeComponent отображается на экране, создается новая кнопка handleClick.

2. использовать обратный вызов

хук useCallback запоминает само определение функции. Теперь, когда SomeComponent отображается на экране, useCallback предоставляет вам кэшированную версию метода handleClick.

import { useCallback } from 'react';
function SomeComponent(props) {
  const handleClick = useCallback(() => {
    console.log('user clicked button');
  }, []);  // function get recreated when dependencies change, here the array is empty meaning the function will never be recreated
  return <button onClick={handleClick}>Click the Button!</button>;
}

При использовании useCallback метод handleClick создается только один раз и кэшируется реакцией, а кэшированная копия всегда будет использоваться, что повышает производительность.

Эта статья предоставлена ​​вам DeadSimpleChat, Chat API и SDK для вашего сайта и приложений.

Основное отличие

  • Непосредственное создание функции приведет к созданию нового экземпляра функции каждый раз, когда компонент перерисовывается.
  • useCallback кэширует функцию и повторно использует ее определение, таким образом каждый раз предоставляя кэшированную копию.

Обновление состояния из запомненного обратного вызова

В React вы можете обновить состояние компонента с помощью функции setState.

Есть два варианта, когда вам нужно обновить состояние на основе предыдущего состояния.

  1. Чтение текущего состояния изнутри обратного вызова
  2. использование функции обновления

Чтение текущего состояния изнутри обратного вызова

const handleTodo = useCallback((todoText) => {
  const newTodo = { id: nextId++, todoText };
  setTodos([...todos, newTodo]);
}, [todos]);

Здесь

  • Мы запоминаем или кэшируем функцию handleTodo с помощью useCallback.
  • переменная состояния todos указана как зависимость, поскольку мы считываем состояние напрямую
  • Здесь каждый раз, когда задачи изменяются, будет создана новая версия handleTodo.

использование функции обновления без прямой зависимости от состояния

const handleAddTodo = useCallback((text) => {
  const newTodo = { id: nextId++, text };
  setTodos(todos => [...todos, newTodo]);
}, []);
  • Здесь вместо чтения todos внутри непосредственного чтения задач и, следовательно, создания необходимости добавления его в качестве зависимости в массив зависимостей useCallback
  • мы предоставляем функцию обновления. Этот модуль обновления func принимает предыдущие задачи в качестве аргумента и вычисляет новое состояние задач.
  • Таким образом, преимущество здесь заключается в том, что handleTodo теперь больше не нужно обновлять при каждом изменении задач, что позволяет больше использовать кэшированную версию и повышать производительность.

Предотвращение слишком частого срабатывания эффекта

Много раз вы использовали крючок useEffect, и вам нужно было вызвать функцию внутри хука.

Это создает проблему: каждое значение должно быть объявлено в массиве зависимостей.

Чтобы оптимизировать useEffect и рендеринг компонентов в целях повышения производительности, вы можете обернуть вызываемую функцию из useEffect в useCallback.

Оптимизация пользовательского хука

Хуки — это функции, предназначенные для инкапсуляции повторно используемых функций. Вот почему вы также можете создавать собственные перехватчики, это способствует повторному использованию кода и удобству сопровождения.

  1. Необходимость оптимизации в пользовательских хуках

Существует необходимость оптимизации пользовательских перехватчиков, возвращающих функции.

Это связано с тем, что эти перехватчики могут стать проблематичными для компонентов, у которых возвращаемая функция является зависимостью.

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

2. useCallback для стабильных функций

Вы можете использовать перехватчик useCallback для кэширования этих функций, тем самым оптимизируя производительность.

Заключение

В этой статье мы узнали о хуке реакции useCallback с примерами его использования и примерами.

Эта статья была первоначально опубликована в блоге DeadSimpleChat: React useCallback: Руководство, варианты использования и примеры

Надеюсь, вам понравилась статья. Спасибо за чтение.