Hook di ottimizzazione delle prestazioni useCallback in React
In questa lezione esamineremo il seguente
hook per l'ottimizzazione delle prestazioni
useCallback.
L'hook useCallback è simile all'API useMemo,
la differenza è che il primo
memorizza nella cache un valore tra i momenti di rerendering
dello schermo, mentre il secondo - una callback.
Questo ci permette di non rieseguire funzioni
che richiedono molte risorse quando non è necessario e può
essere utilizzato quando si
passa una funzione
ai componenti figli.
Analizziamo più in dettaglio con un esempio.
Per iniziare, creiamo un componente App
e al suo interno creiamo uno stato num:
const [num, setNum] = useState(0);
Creiamo un pulsante, al click
del quale num aumenta
di 1, e un paragrafo in cui
visualizzeremo il valore di num:
return (
<div>
<button onClick={() => setNum(num + 1)}>click</button>
<p>clicks: {num}</p>
</div>
);
Ora, supponiamo che in
App venga visualizzato un altro elenco
di elementi, che aggiungeremo
premendo un altro pulsante. Per memorizzare
gli elementi di questo elenco, creeremo
lo stato items:
const [items, setItems] = useState([]);
E poi scriviamo la funzione addItem
per aggiungerli:
function addItem() {
setItems([...items, 'new item']);
}
Ora scriviamo il codice per visualizzare
gli elementi dell'elenco e spostiamolo in un componente
figlio Items, che riceverà come
props l'array degli elementi
e la funzione per aggiungerli. Non dimentichiamo
di aggiungere un output in console, per vedere
quando il nostro Items verrà
rerenderizzato:
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;
Posizioniamo Items alla fine del componente
App e passiamogli l'array
items e la funzione per aggiungere
elementi addItem:
return (
<>
<div>
<button onClick={() => setNum(num + 1)}>click</button>
<p>clicks: {num}</p>
<br />
</div>
<Items items={items} addItem={addItem} />
</>
);
Ora clicchiamo sui pulsanti
e verifichiamo che num aumenti e
che nuovi elementi vengano aggiunti alla lista.
Aprendo la console, vedremo che
il nostro elenco viene rerenderizzato ogni
volta, anche se clicchiamo sul pulsante
che aumenta num.
Se abbiamo un piccolo elenco, va tutto
bene, ma se si prevede che sarà
voluminoso e che contenga molte altre cose?
Nessun problema - direte voi, perché nella lezione
precedente abbiamo esaminato l'API memo,
proprio per evitare rerendering non necessari
del componente.
Quindi avvolgiamo il nostro componente
Items in memo e il gioco è fatto.
A proposito, questo può essere fatto direttamente
all'esportazione di Items:
export default memo(Items);
Non dimentichiamo di importare memo:
import { memo } from 'react';
Ora apriamo la console e clicchiamo
sui pulsanti. Tutti gli sforzi sono stati inutili! Abbiamo
memorizzato il componente, ma quando si clicca
sul pulsante 'click' il componente
Items viene comunque
rerenderizzato ogni volta.
Il fatto è che quando il componente genitore
si rerenderizza, le sue funzioni
vengono ricreate da zero - questo vale anche per la nostra
funzione addItem, che passiamo a
Items.
Proprio in questo momento ci viene in aiuto l'hook
useCallback. Applichiamolo.
Per iniziare, importiamolo in
App:
import { useCallback } from 'react';
Poi trasformiamo la semplice dichiarazione di funzione
addItem in una
Function Expression, specifichiamo come
primo parametro per useCallback
la nostra funzione sotto forma di callback. Come secondo
parametro, tra parentesi quadre, specifichiamo
le dipendenze - tutte le variabili reattive
coinvolte nella funzione, nel nostro caso
l'array items:
const addItem = useCallback(() => {
setItems(() => [...items, 'New item']);
}, [items]);
Fatto! In questo modo abbiamo memorizzato nella cache
la funzione. Clicchiamo di nuovo sui pulsanti e
vediamo che ora, quando si clicca sul pulsante
'click', il nostro componente figlio non
viene più rerenderizzato.
Crea un componente App, inserisci
al suo interno un paragrafo con del testo. Crea
uno stato con valore iniziale 'text'
e visualizzalo nel paragrafo. Fai in modo che al click
sul paragrafo, al suo testo venga
aggiunto un punto esclamativo alla fine.
Crea un componente figlio Products,
in cui avrai un pulsante per aggiungere
un nuovo prodotto. Inseriscilo in App.
Nel componente genitore, crea uno stato
con un array di prodotti e una funzione di aggiunta
di un nuovo prodotto. Passali come
props al componente figlio, visualizza in esso
l'array passato come lista ul.
In Products, visualizza nella console il testo
'products render'.
Avvolgi Products in memo.
Clicca sul paragrafo e sul pulsante. Verifica
che, cliccando sul paragrafo, il componente figlio
venga comunque rerenderizzato.
Memorizza nella cache la funzione per aggiungere
prodotti, avvolgendola nell'hook useCallback.
Clicca sul paragrafo e sul pulsante. Verifica
che, cliccando sul paragrafo, il componente figlio
non venga più rerenderizzato.