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

У овом лекцији ћемо размотрити следећи хук за оптимизацију перформанси useCallback.

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

Хајде да детаљније размотримо на примеру. Прво, направимо компонент App и у њему креирајмо state num:

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

Нека имамо дугме, при клику на које се num повећава за 1, и пасус у коме ћемо исписати вредност num:

App.jsxreturn (
  <div>
    <button onClick={() => setNum(num + 1)}>кликни</button>
    <p>кликова: {num}</p>
  </div>
);

Сада, претпоставимо да у App имамо још један приказ листе са ставкама, коју ћемо допуњавати притиском на друго дугме. За чување ставки ове листе креирајмо state items:

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

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

App.jsxfunction addItem() {
  setItems([...items, 'нова ставка']);
}

Хајде сада да напишемо код за приказ ставки листе и да га издвојимо у компонент дете Items, који ће у виду пропсова добијати низ ставки и функцију за њихово додавање. Не заборавимо да додамо испис у конзолу, да бисмо видели када ће се наш Items поново исцртати:

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

  const result = items.map((item, index) => {
    return <p key={index}>{item}</p>;
  });
  
  console.log('Items се исцртава');
  
  return (
    <div>
      <h3>Наше ставке</h3>
      {result}
      <button onClick={addItem}>додај ставку</button>
    </div>
  );
}

export default Items;

Поставимо Items на крај компонента App и проследимо му низ items и функцију за додавање ставки addItem:

App.jsxreturn (
  <>
    <div>
      <button onClick={() => setNum(num + 1)}>кликни</button>
      <p>кликова: {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';

Сада отворимо конзолу и кликнимо на дугмад. Свa trud је узалуд! Ми смо мемоизирали компонент, али при притиску на дугме 'кликни' компонента Items се и даље понио исцртава сваки пут.

Ствар је у томе што када се родитељска компонента поново исцртава, њене функције се поново креирају - то се односи и на нашу функцију addItem, коју прослеђујемо у Items.

Управо у овом тренутку ће нам помоћи хук useCallback. Хајде да га применимо. Прво, увезимо га у App:

App.jsximport { useCallback } from 'react';

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

App.jsxconst addItem = useCallback(() => {
  setItems(() => [...items, 'Нова ставка']);
}, [items]);

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

Задание 1

Направите компонент App, поставите у њега пасус са текстом. Креирајте state са почетном вредношћу 'текст' и испишите га у пасусу. Нека се при клику на пасус његовом тексту додаје узвичник.

Задание 2

Направите компонент дете Products, у којем ћете имати дугме за додавање новог производа. Поставите га у App. У родитељској компоненти креирајте state са низом производа и функцију за додавање новог производа. Проследите их у компонент дете као пропсове, испишите у њему прослеђени низ у виду листе ul.

Задание 3

У Products испишите у конзолу текст 'products се исцртава'. Омотајте Products у memo. Кликните на пасус и дугме. Уверите се да при клику на пасус компонента дете и даље се поново исцртава.

Задание 4

Кеширајте функцију за додавање производа, обавијајући је у хук useCallback. Кликните на пасус и дугме. Уверите се да при клику на пасус, компонента дете више не понавља исцртавање.