Владислав Калачев

Учим useCallback на примерах — React Hooks

4 572

Доброго времени суток, друзья. И вновь поговорим про React Hooks. Сегодня мы рассмотрим хук useCallback. В чем его польза? Какие особенности? Давайте обо всем по порядку.

Видео на эту тему.

Хук useCallback для мемоизации? 

Хук useCallback похож на хук useMemo (больше информации по данному хуку вы можете получить тут) с тем отличием, что он возвращает мемоизированный колбэк, который будет обновлен, только если одна из зависимостей будет изменена.

Пример:


const memoized = useCallback(
  () => {
    foo(a, b);
  },
  [a, b],
);

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

Хук useCallback на практике

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

Пример:


import React, { useEffect } from "react";

export default function Child({ updateOne, updateTwo }) {
  useEffect(() => {
    updateOne();
  }, [updateOne]);

  useEffect(() => {
    updateTwo();
  }, [updateTwo]);

  return <div className="App" />
}

Поместим данный компонент в App.js, в котором будет храниться состояние двух счетчиков (использую хук useState), также там будут находиться две кнопки, при клике на которые вызываются функции для изменения значения счетчиков.

Самая интересная часть, это две функции, находящиеся в App.js, которые передаются дочернему компоненту Child. Функции будут просто вызывать соnsole.log и их единственное различие будет заключаться в том, что одна из них будет обернута хуком useCallback и иметь в зависимости состояние одного из счетчиков.

Пример:


import React, { useEffect, useState, useCallback } from "react";
import Child from "./child";
import "./styles.css";

export default function App() {
  const [counter, setCounter] = useState(0);
  const [counterTwo, setCounterTwo] = useState(0);

  const updateOne = () => {
    console.log(
      "Я не мемоизирован"
    );
  };

  const updateTwo = useCallback(() => {
    console.log(
      "Я мемоизирован!"
   );
  }, [counter]);

  return (
    <div className="App">
      <button onClick={() => setCounter(counter + 1)}>One</button>
      <br />
        <button onClick={() => setAnotherState(counterTwo + 1)}>
        Two
      </button>
      <Child updateOne ={updateOne} updateTwo ={updateTwo} />
    </div>
  );
}

Если кликнуть на кнопку “One”, то можно увидеть вызов одновременно двух консолей, при этом, если кликнуть на кнопку “Two”, то будет виден вызов только одной консоли с текстом «Я не мемоизирован». Почему так происходит? Давайте рассмотрим этот процесс подробнее.

По клику на кнопку “One” происходит вызов функции setCounter, в которой мы меняем значение counter c прибавлением к нему единицы. Функция updateTwo мемоизирована и ее новая копия появляется только в момент, когда изменяется значение состояния counter. Как следствие мы получаем нашу обновленную функцию и далее по ссылке в компоненте Child происходит проверка на равенство в useEffect. Ссылка на старую функцию изменилась, что приводит к принудительному перерендереванию дочернего компонента с вызовом самой функции.

При нажатии на кнопку “Two” также происходит изменение счетчика counterTwo на одну единицу. Но данное состояние не указано в массиве зависимостей хука useCallback. Это означает, что функция updateTwo не обновится, а React будет использовать ее закэшированную версию. При дальнейшем сравнении в компоненте Child ссылка на функцию останется прежней, что не приведет к ее вызову.

Заключение

Сегодня мы разобрали на примере счетчик с родительским компонентом и как можно мемоизироивать функции, используя хук useCallback. Использование разработчиками хуков useMemo и useCallback позволяет в значительной степени оптимизировать приложения. Надеюсь, что данный материал был вам полезен. Учитесь, думайте, пишите код. Удачного кодинга, друзья!

Полезные материалы

Офф док: https://ru.reactjs.org/docs/hooks-reference.html#usecallback

Подписывайтесь на наш канал в Telegram и на YouTube для получения самой последней и актуальной информации. 

Комментарии:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *