Хук оптимизации производительности useMemo в React

Первый хук для оптимизации производительности, который мы рассмотрим - это useMemo.

Данный хук помогает кэшировать результаты ресурсоемких операций между моментами перерисовки экрана и соответственно может помочь избежать лишних объемных вычислений. Такое кэширование называют еще мемоизацией.

Посмотрим, как это работает. Давайте создадим компонент с кнопкой и заголовком h3:

App.jsxreturn (
  <div>
    <h3>Text</h3>
    <button>click</button>
  </div>
);

А сейчас сделаем так, чтобы по клику на заголовок его цвет менялся с оранжевого на зеленый и обратно. Для начала заведем стейт isGreen:

App.jsxconst [isGreen, setIsGreen] = useState(false);

Добавим в атрибут style заголовка условие изменения цвета заголовку и навесим обработчик клика:

App.jsx<h3 onClick={() => setIsGreen(!isGreen)}
  style={{ color: isGreen ? 'green' : 'orangered' }}
>Text</h3>

Пусть у нас также будет какое-то значение, которое будет увеличиваться при клике по нашей кнопочке на единицу. Заведем для него стейт:

App.jsxconst [num, setNum] = useState(0);

Добавим обработку клика по кнопке:

App.jsx<button onClick={() => setNum(num + 1)}>
  clicks
</button>

Пусть также у нас будет некая функция square, которая будет возвращать квадрат значения num. Результат вызова функции мы будем записывать в переменную result:

App.jsxconst result = square(num);

function square(num) {
  return num * num;
}

Выведем result в тексте кнопки:

App.jsx<button onClick={() => setNum(num + 1)}>
  clicks: {result}
</button>

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

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

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

App.jsxfunction square(num) {
  let startTime = performance.now();
  
  while (performance.now() - startTime < 500) {
    // Just do nothing ...
  }
  
  return num * num;
}

Понажимайте теперь на заголовок. Получается, что теперь из-за долгой работы функции square (а кнопку-то мы не трогаем) мы целую вечность должны ждать, чтобы у заголовка поменялся цвет!

Здесь к нам на помощь и придет хук useMemo. Для этого, нам нужно первым параметром передать функцию, вычисляющую значение, которое мы хотим кэшировать, данная функция должна быть чистой и не принимать никаких параметров. А вторым параметром - зависимости в квадратных скобках, другими словами, все реактивные значения, которые участвуют в коде функции. Таким образом, в result мы теперь впишем такую конструкцию:

App.jsxconst result = useMemo(() => square(num), [num]);

Снова понажимаем на заголовок. Теперь, если мы не трогаем кнопку с вычислениями и не меняем таким образом значение стейта num, то ничего не пересчитывается, и React отображает кэшированное значение в кнопке, поэтому наш заголовок быстренько меняет свой цвет.

Задание 1

Создайте компонент App, разместите в нем абзац. Заведите стейт text с начальным значением 'react', пусть значение стейта отображается в качестве текста абзаца. Пусть по клику на абзац, ему в конец текста добавляется восклицательный знак.

Задание 2

Заведите еще один стейт num, с начальным значением 0. Разместите в App еще один абзац. Сделайте так, чтобы при клике по нему num увеличивалась на 1.

Задание 3

А теперь добавьте в App функцию triple, которая в качестве параметра принимает num и возвращает его утроенное значение. Положите результат вызова функции в переменную result. Отобразите result в качестве текста второго абзаца. Покликайте по очереди на абзацы, отметьте, как медленно добавляются восклицательные знаки.

Задание 4

Исправьте ситуацию, обернув медленную функцию triple в useMemo.