Хук аптымізацыі прадукцыйнасці useCallback у React

У гэтым уроку мы разгледзім наступны хук для аптымізацыі прадукцыйнасці useCallback.

Хук useCallback падобны да API useMemo, адрозненне заключаецца ў тым, што першы кэшуе значэнне паміж маментамі перамалёўкі экрана, а другі - колбэк. Гэта дазваляе нам не перазапускаць рэсурсазатратныя функцыі, калі гэта не патрабуецца і можа выкарыстоўвацца пры перадачы функцыі у даччыныя кампаненты.

Давайце разбярэмся падрабязней на прыкладзе. Для пачатку створым кампанент App і завядзем у ім стэйт num:

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

Хай у нас будзе кнопка, па кліку на якую num павялічваецца на 1, і абзац, у якім мы будзем выводзіць значэнне num:

App.jsxreturn (
  <div>
    <button onClick={() => setNum(num + 1)}>click</button>
    <p>clicks: {num}</p>
  </div>
);

А зараз, выкажам здагадку, што ў нас у App выводзіцца яшчэ які-небудзь спіс з элементамі, які мы будзем дапаўняць па націску іншай кнопкі. Для захоўвання элементаў гэтага спісу мы завядзем стейт items:

App.jsxconst [items, setItems] = useState([]);

І затым напiшам функцыю addItem для іх дадавання:

App.jsxfunction addItem() {
  setItems([...items, 'new item']);
}

Цяпер давайце напiшам код для адлюстравання элементаў спісу і вынясем яго ў даччыны кампанент Items, які ў выглядзе пропсаў будзе атрымліваць масіў элементаў і функцыю для іх дадавання. Не забудзем дадаць вывад у кансоль, каб бачыць калі наш Items будзе перамалёўвацца:

Items.jsxfunction Items({ items, addItem }) {

  const result = items.map((item, index) => {
    return <p key={index}>{item}</p>;
  });
  
  console.log('Items render');
  
  return (
    <div>
      <h3>Our items</h3>
      {result}
      <button onClick={addItem}>add item</button>
    </div>
  );
}

export default Items;

Размясцім Items у канцы кампанента App і будзем перадаваць яму масіў items і функцыю для дадавання элементаў addItem:

App.jsxreturn (
  <>
    <div>
      <button onClick={() => setNum(num + 1)}>click</button>
      <p>clicks: {num}</p>
      <br />
    </div>
    <Items items={items} addItem={addItem} />
  </>
);

А цяпер панаціскаем на кнопачкі і пераканаемся, што num расце і новыя элементы дадаюцца ў спіс. А адкрыўшы кансоль, мы ўбачым, што наш спіс перамалёўваецца кожны раз, нават калі мы клікаем па кнопцы, якая павялічвае num.

Калі ў нас маленькі спісок, то ўсё ў парадку, а калі меркаецца, што ён будзе аб'ёмным і там многа чаго яшчэ ёсць? Не бяда - скажаце вы, бо на мінулым уроку мы разгледзелі API memo, каб менавіта пазбягаць непотрэбных перамалёвак кампанента.

Дык давайце аберым наш кампанент Items у memo і ўсе справы. Дарэчы гэта можна зрабіць проста пры экспарце Items:

Items.jsxexport default memo(Items);

Не забудзем імпартаваць memo:

Items.jsximport { memo } from 'react';

А цяпер адкрыем кансоль і панаціскаем на кнопачкі. Усе намаганні дарма! Мы мемаізавалі кампанент, але пры націску на кнопку 'click' кампанент Items усё роўна перамалёўваецца кожны раз.

Справа ў тым, што калі бацькоўскі кампанент перамалёўваецца, яго функцыі пераствараюцца нанава - гэта тычыцца і нашай функцыі addItem, якую мы перадаем у Items.

Менавіта ў гэты момант нам дапаможа хук useCallback. Давайце прымянім яго. Для пачатку імпартуем яго ў App:

App.jsximport { useCallback } from 'react';

Затым пераробім простае аб'яўленне функцыі addItem у Function Expression, пакажам у якасці першага параметра для useCallback нашу функцыю ў выглядзе колбэка. Другім параметрам у квадратных дужках пакажам залежнасці - усе рэактыўныя зменныя, якія ўдзельнічаюць у функцыі, у нашым выпадку гэта масіў items:

App.jsxconst addItem = useCallback(() => {
  setItems(() => [...items, 'New item']);
}, [items]);

Гатова! Такім чынам мы закэшавалі функцыю. Націскаем зноў на кнопачкі і бачым, што цяпер пры націску на кнопку 'click' наш даччыны кампанент не перамалёўваецца.

Задание 1

Стварыце кампанент App, змясціце ў яго абзац з тэкстам. Завядзіце стейт з пачатковым значэннем 'text' і вывядзіце яго ў абзацы. Хай па кліку на абзац яму ў канец тэксту дадаецца клічнік.

Задание 2

Стварыце даччыны кампанент Products, у якім у вас будзе кнопка для дадавання новага прадукту. Размясціце яго ў App. У бацькоўскім кампаненце стварыце стэйт з масівам прадуктаў і функцыю дадавання новага прадукту. Перадайце іх у якасці пропсаў у даччыны, вывядзіце ў ім перададзены масіў у выглядзе спісу ul.

Задание 3

У Products вывядзіце ў кансоль тэкст 'products render'. Аберыце Products у memo. Паклікайце на абзац і кнопку. Пераканайцеся, што пры кліку на абзац даччыны кампанент усё роўна перамалёўваецца.

Задание 4

Закэшавайце функцыю для дадавання прадуктаў, абернуўшы яе ў хук useCallback. Паклікайце на абзац і кнопку. Пераканайцеся, што пры кліку на абзац, даччыны кампанент больш не перамалёўваецца.