Хук за оптимизација на перформансите useCallback во React

Во оваа лекција ќе го разгледаме следниот hook за оптимизација на перформансите useCallback.

Hook-от useCallback е сличен на API-то useMemo, разликата е во тоа што првиот кешира вредност помеѓу моментите на прецртување на екранот, а вториот - callback. Ова ни овозможува да не рестартираме ресурсно-интензивни функции кога тоа не е потребно и може да се користи при пренос на функција во детските компоненти.

Ајде да разбереме подетално на пример. За почеток да креираме компонента App и да воспоставиме state 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 се прикажува уште некоја листа со елементи, која ќе ја дополнуваме со притискање на друго копче. За чување на елементите од оваа листа ќе воспоставиме state items:

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

И потоа ќе напишеме функција addItem за нивно додавање:

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

Сега ајде да го напишеме кодот за приказ на елементите од листата и да го издвоиме во детска компонента Items, која како пропертија ќе добие низа од елементи и функција за нивно додавање. Да не заборавиме да додадеме print во конзола, за да видиме кога нашата 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.

Токму во овој момент hook-от useCallback ќе ни помогне. Ајде да го примениме него. За почеток да го импортираме во App:

App.jsximport { useCallback } from 'react';

Потоа да ја претвориме едноставната декларација на функцијата addItem во Function Expression, да наведеме како прв параметар за useCallback нашата функција во вид на callback. Вториот параметар во квадратни загради ќе ги наведеме зависностите - сите реактивни променливи, кои учествуваат во функцијата, во нашиот случај тоа е низата items:

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

Готово! На овој начин ја кеширавме функцијата. Кликнуваме повторно на копчињата и гледаме дека сега при притискање на копчето 'click' нашата детска компонента не се прецртува.

Задание 1

Креирајте компонента App, поставете во неа параграф со текст. Воспоставете state со почетна вредност 'text' и прикажете ја во параграфот. Нека при клик на параграфот на крајот од текстот му се додава извичник.

Задание 2

Креирајте детска компонента Products, во која ќе имате копче за додавање нов продукт. Поставете ја во App. Во родителската компонента креирајте state со низа на производи и функција за додавање нов продукт. Пренесете ги во како пропертија во детската, прикажете ја во неа пренесената низа како листа ul.

Задание 3

Во Products прикажете во конзола текст 'products render'. Завиткајте ја Products во memo. Кликнувајте на параграфот и копчето. Уверете се, дека при клик на параграфот детската компонента сепак се прецртува.

Задание 4

Закеширајте ја функцијата за додавање продукти, завиткајќи ја во hook-от useCallback. Кликнувајте на параграфот и копчето. Уверете се, дека при клик на параграфот, детската компонента повеќе не се прецртува.