Der Performance-Optimierungs-Hook useCallback in React
In dieser Lektion betrachten wir den folgenden
Hook zur Leistungsoptimierung:
useCallback.
Der Hook useCallback ähnelt der API useMemo,
der Unterschied besteht darin, dass der erste
einen Wert zwischen den Bildschirm-Neurenderings
zwischenspeichert, und der zweite - einen Callback.
Dies erlaubt uns, ressourcenintensive
Funktionen nicht neu auszuführen, wenn es nicht nötig ist, und kann
bei der
Übergabe einer Funktion
an Kindkomponenten verwendet werden.
Lassen Sie uns das an einem Beispiel genauer verstehen.
Erstellen wir zunächst eine Komponente App
und legen darin einen State num an:
const [num, setNum] = useState(0);
Stellen wir uns eine Schaltfläche vor, bei einem Klick
auf die num um
1 erhöht wird, und einen Absatz, in dem wir
den Wert num ausgeben:
return (
<div>
<button onClick={() => setNum(num + 1)}>click</button>
<p>clicks: {num}</p>
</div>
);
Nehmen wir nun an, dass in
App noch eine Art Liste
mit Elementen ausgegeben wird, die wir durch Drücken
einer anderen Schaltfläche ergänzen werden. Zum Speichern
der Elemente dieser Liste legen wir einen
State items an:
const [items, setItems] = useState([]);
Und dann schreiben wir eine Funktion addItem
zu deren Hinzufügung:
function addItem() {
setItems([...items, 'new item']);
}
Schreiben wir nun den Code zur Anzeige
der Listenelemente und lagern ihn in eine Kindkomponente
Items aus, die als
Props das Array der Elemente
und die Funktion zu deren Hinzufügung erhält. Vergessen wir nicht,
eine Ausgabe in der Konsole hinzuzufügen, um zu sehen,
wann sich unser Items
neu rendert:
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;
Platzieren wir Items am Ende der Komponente
App und übergeben ihr das Array
items und die Funktion zum Hinzufügen
von Elementen addItem:
return (
<>
<div>
<button onClick={() => setNum(num + 1)}>click</button>
<p>clicks: {num}</p>
<br />
</div>
<Items items={items} addItem={addItem} />
</>
);
Klicken wir nun auf die Schaltflächen
und stellen sicher, dass num wächst und
neue Elemente zur Liste hinzugefügt werden.
Wenn wir die Konsole öffnen, werden wir sehen, dass
sich unsere Liste jedes Mal neu rendert,
sogar wenn wir auf die Schaltfläche klicken,
die num erhöht.
Wenn wir eine kleine Liste haben, ist alles
in Ordnung, aber was, wenn davon auszugehen ist, dass sie
umfangreich sein wird und dort noch viel mehr vorhanden ist?
Kein Problem - werden Sie sagen, denn in der letzten
Lektion haben wir die API memo kennengelernt,
um genau unnötige Neuzeichnungen
einer Komponente zu vermeiden.
Also wickeln wir unsere Komponente
Items in memo ein und das war's.
Übrigens kann dies direkt
beim Export von Items gemacht werden:
export default memo(Items);
Vergessen wir nicht, memo zu importieren:
import { memo } from 'react';
Öffnen wir nun die Konsole und klicken auf
die Schaltflächen. Die ganze Mühe war umsonst! Wir
haben die Komponente memoisiert, aber beim Drücken
der Schaltfläche 'click' wird die Komponente
Items trotzdem
jedes Mal neu gerendert.
Der Grund dafür ist, dass wenn die Elternkomponente
sich neu rendert, ihre Funktionen
neu erstellt werden - das betrifft auch unsere
Funktion addItem, die wir an
Items übergeben.
Genau in diesem Moment hilft uns der Hook
useCallback. Wenden wir
ihn an. Importieren wir ihn zunächst in
App:
import { useCallback } from 'react';
Dann wandeln wir die einfache Funktionsdeklaration
addItem in einen
Function Expression um, geben als
ersten Parameter für useCallback
unsere Funktion in Form eines Callbacks an. Als zweiten
Parameter geben wir in eckigen Klammern
die Abhängigkeiten an - alle reaktiven Variablen,
die in der Funktion verwendet werden, in unserem Fall
ist das das Array items:
const addItem = useCallback(() => {
setItems(() => [...items, 'New item']);
}, [items]);
Fertig! Auf diese Weise haben wir die
Funktion zwischengespeichert.
Wir klicken erneut auf die Schaltflächen und
sehen, dass sich nun beim Drücken der Schaltfläche
'click' unsere Kindkomponente nicht
mehr neu rendert.
Erstellen Sie eine Komponente App, platzieren Sie
darin einen Absatz mit Text. Legen Sie
einen State mit dem Anfangswert 'text'
an und geben Sie ihn im Absatz aus. Beim Klick
auf den Absatz soll an sein Textende
ein Ausrufezeichen angehängt werden.
Erstellen Sie eine Kindkomponente Products,
in der Sie eine Schaltfläche zum Hinzufügen
eines neuen Produkts haben. Platzieren Sie sie in App.
Erstellen Sie in der Elternkomponente einen State
mit einem Array von Produkten und eine Funktion zum Hinzufügen
eines neuen Produkts. Übergeben Sie sie als
Props an die Kindkomponente, geben Sie darin
das übergebene Array als Liste ul aus.
Geben Sie in Products den Text
'products render' in der Konsole aus.
Wickeln Sie Products in memo ein.
Klicken Sie auf den Absatz und die Schaltfläche. Stellen Sie sicher,
dass bei einem Klick auf den Absatz die Kindkomponente
trotzdem neu gerendert wird.
Speichern Sie die Funktion zum Hinzufügen
von Produkten zwischen, indem Sie sie in den Hook useCallback einwickeln.
Klicken Sie auf den Absatz und die Schaltfläche. Stellen Sie sicher,
dass sich bei einem Klick auf den Absatz die Kindkomponente
nicht mehr neu rendert.