Когда же нужно использовать UseCallback

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Все мы знаем что с появлением React 16.8 в нашу жизнь пришли хуки. Да действительно, они упрощают процесс написания кода, используя функциональные компоненты в сравнении с классами. Но некоторые хуки вызывают много вопросов у разработчиком. В данной статье мы рассмотрим на примерах, когда же нужно использовать UseCallback, а когда можно обойтись без него.

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

const memoizedCallback = useCallback(
 () => {
    doSomething(a, b);
  },
  [a, b],
);

Давайте рассмотрим пример.

У нас имеется страница с инпутом, списком отображения элементов и кнопкой, которая добавляет в список введенный элемент. При клике на сам элемент списка он будет удаляться. Дефолтный список возьмём из 5 городов который мы будем редактировать.

const listOfCities = ['Beijing','Tokyo','Kinshasa','Moscow','Jakarta'];

const Page = () => {
  const [name, setName] = useState('');
	const [list, setList] = useState(listOfCities);
  
  const handleClick = () => {
      setList([...list, name]);
      setName('');
  };

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setName(event.target.value);
  };
  
  const handleRemoveClick = (item: string) => {
    const filteredList = list.filter((listItem) => listItem !== item);

    setList(filteredList);
  };
  
  console.log('Page render');
  
  return (
    <div>
      <input type="text" value={name} onChange={handleChange}/>
      <button onClick={handleClick}>Add</button>
			<CitiesList list={list} onRemoveClick={handleRemoveClick}/>
    </div>
  );
};

И компоненты отображения списка

export const CitiesList = ({list, onRemoveClick}) => {
  console.log('List render');
  
  return list.map((item) => {
    return <City key={item} city={item} onRemoveClick={onRemoveClick} />;
  });
};


export const City = ({city, onRemoveClick}) => {
  const handleCityClick = () => onRemoveClick(сity);

  console.log('City render');

  return <div onClick={handleCityClick}>{city}</div>
});

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

Откроем консоль и увидим

Кажется все логично, произошел рендер страницы, компонента списка и его 5 элементов.

Начинаем вводить в инпут символы и видим что на каждый введенный символ происходит аналогичный рендер во всех компонентах. Проблема понятна, изменения значения инпут (state name), вызывает рендер всех дочерних компонентах.

У нас есть три callback функции, которые теоретически можно обернуть в UseCallback. Разберем их по отдельности.

Обернув handleClick, handleChange или handleCityClick в UseCallback, увы это никак не улучшит перфоманс. UseCallback это тоже функция которая при каждом рендер, будет заново сравнивать зависимости и возвращать новую или старую ссылку на функцию.

Обернув handleRemoveClick в UseCallback, уменьшит количество рендеров при условии что мы обернем SitiesList в React.memo, подробнее о нем можно посмотреть в документации.

const handleRemoveClick = useCallback((item: string) => {
    const filteredList = list.filter((listItem) => listItem !== item);

    setList(filteredList);
}, [list]);
const CitiesList = React.memo(({ list, onRemoveClick }) => {
  console.log("List render");

  return list.map((item) => {
    return <City key={item} item={item} onRemoveClick={onRemoveClick} />;
  });
});

Теперь при вводе символов в инпуте, компонент СitiesList не перерендеривается. На список из 5 элементов улучшение перфоманса будет не столь большим, но наглядно показывает как его можно будет улучшить в случае необходимости.

В данном примере можно было вынести инпут с кнопкой и его state в отдельный компонент, тогда необходимость использования UseCallback уже была бы не актуальной, поэтому не делайте оптимизаций производительности до тех пор, пока это действительно не потребуется.

Какие из этого можно сделать вывод

UseCallback нужная и полезная вещь, которая помогает улучшить перформанс. Но её применение не всегда актуально. Используйте UseCallback для функций, которые передаются компонентам с большими затратами памяти для отображения. Делайте оптимизацию производительности после написания и рефакторинга кода.

Надеюсь, эта статья была вам полезной. Всем пока :)

Источник: https://habr.com/ru/post/590577/


Интересные статьи

Интересные статьи

Законодательные инициативы, особенно в США, частенько становились предметом неудержимых споров и искреннего непонимания. Вот и сегодня хочу рассказать вам о билле 246 — п...
О витамине D, витамине К2 и сложностях внутривидовой конкуренции Внутривидовая борьба всегда самая жестокая. Представители одного вида нуждаются в одних и тех же ресурсах: территор...
Advanced Gun System (слева) создана как замена 16-дюймовых орудий линкоров (справа). Если не считать управляемых по GPS снарядов, цифровые технологи системы управления огнём AGS вып...
Захотелось как-то мне использовать в Linux внедряемые ресурсы, причём, автоматически. В общем, задача такая: Имеется Eclipse проект программы на C++. ОС: Linux Ubuntu. Компилятор: G++ В проект...
* для создания динамической страницы Не так давно 12.12.2018 был анонсирован выход новой библиотеки для фанатов phoenix framework под названием Phoenix LiveView. Я бы хотел поделиться с вами в...