Хук за оптимизация на производителността useCallback в React
В този урок ще разгледаме следващия
хук за оптимизация на производителността
useCallback.
Хукът useCallback е подобен на API useMemo,
разликата е, че първият
кешира стойност между моментите на прерисуване
на екрана, а вторият - callback функция.
Това ни позволява да не презапускаме ресурсоемките
функции, когато не е необходимо и може
да се използва при
предаване на функция
в дъщерни компоненти.
Нека разберем по-подробно с пример.
Първо създаваме компонент App
и създаваме в него състояние num:
const [num, setNum] = useState(0);
Нека имаме бутон, при клик
върху който num се увеличава
с 1, и параграф, в който
ще извеждаме стойността на num:
return (
<div>
<button onClick={() => setNum(num + 1)}>click</button>
<p>clicks: {num}</p>
</div>
);
А сега, да предположим, че в
App се извежда и някакъв списък
с елементи, който ще допълваме
при натискане на друг бутон. За съхранение
на елементите от този списък ще създадем
състояние items:
const [items, setItems] = useState([]);
И след това ще напишем функция addItem
за тяхното добавяне:
function addItem() {
setItems([...items, 'new item']);
}
Сега нека напишем код за показване
на елементите от списъка и да го изнесем в дъщерен
компонент Items, който като
пропсове ще получава масив от елементи
и функция за тяхното добавяне. Да не забравим
да добавим извеждане в конзолата, за да виждаме
кога нашият Items ще
се прерисува:
function 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:
return (
<>
<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:
export default memo(Items);
Да не забравим да импортираме memo:
import { memo } from 'react';
А сега отваряме конзолата и натискаме
бутоните. Всички усилия напразно! Ние
мемоизирахме компонента, но при натискане
на бутона 'click' компонентът
Items все пак
се прерисува всеки път.
Работата е там, че когато родителският
компонент се прерисува, неговите функции
се пресъздават наново - това се отнася и за нашата
функция addItem, която предаваме в
Items.
Точно в този момент ще ни помогне хукът
useCallback. Нека го приложим.
Първо го импортираме в
App:
import { useCallback } from 'react';
След това преработваме простото деклариране на функцията
addItem в
Function Expression, посочваме като
първи параметър за useCallback
нашата функция под формата на callback. Вторият
параметър в квадратни скоби посочваме
зависимостите - всички реактивни променливи,
участващи във функцията, в нашия случай
това е масивът items:
const addItem = useCallback(() => {
setItems(() => [...items, 'New item']);
}, [items]);
Готово! По този начин ние кеширахме
функцията. Натискаме отново бутоните и
виждаме, че сега при натискане на бутона
'click' нашият дъщерен компонент не
се прерисува.
Създайте компонент App, поставете
в него параграф с текст. Създайте
състояние с начална стойност 'text'
и я изведете в параграфа. Нека при клик
върху параграфа в края на текста
му се добавя удивителен знак.
Създайте дъщерен компонент Products,
в който ще имате бутон за добавяне
на нов продукт. Разположете го в App.
В родителския компонент създайте състояние
с масив от продукти и функция за добавяне
на нов продукт. Предайте ги в
качеството на пропсове в дъщерния, изведете в него
предадения масив като списък ul.
В Products изведете в конзолата текст
'products render'.
Обвийте Products в memo.
Кликнете върху параграфа и бутона. Уверете се,
че при клик върху параграфа дъщерният компонент
все пак се прерисува.
Кеширайте функцията за добавяне
на продукти, като я обвиете в хука useCallback.
Кликнете върху параграфа и бутона. Уверете се,
че при клик върху параграфа, дъщерният компонент
вече не се прерисува.