Learning How to Tame React's useCallback Hook
Veröffentlicht: 2022-04-27Es ist kein Geheimnis, dass React.js in den letzten Jahren sehr beliebt geworden ist. Es ist jetzt die JavaScript-Bibliothek der Wahl für viele der bekanntesten Akteure im Internet, einschließlich Facebook und WhatsApp.
Einer der Hauptgründe für den Aufstieg war die Einführung von Hooks in Version 16.8. Mit React-Hooks können Sie die React-Funktionalität nutzen, ohne Klassenkomponenten schreiben zu müssen. Jetzt sind funktionale Komponenten mit Hooks zur bevorzugten Struktur von Entwicklern für die Arbeit mit React geworden.
In diesem Blogbeitrag gehen wir näher auf einen bestimmten Haken ein – useCallback
– weil er einen grundlegenden Teil der funktionalen Programmierung berührt, der als Memoisierung bekannt ist. Sie werden genau wissen, wie und wann Sie den useCallback
Hook verwenden und das Beste aus seinen leistungssteigernden Fähigkeiten machen können.
Bereit? Tauchen wir ein!
Was ist Memorisierung?
Memoization ist, wenn eine komplexe Funktion ihre Ausgabe speichert, damit sie das nächste Mal mit derselben Eingabe aufgerufen wird. Es ähnelt dem Caching, aber auf einer eher lokalen Ebene. Es kann alle komplexen Berechnungen überspringen und die Ausgabe schneller zurückgeben, da sie bereits berechnet wurde.
Dies kann erhebliche Auswirkungen auf die Speicherzuweisung und Leistung haben, und diese Belastung soll der useCallback
Hook lindern.
useCallback vs. useMemo von React
An dieser Stelle ist es erwähnenswert, dass useCallback
gut mit einem anderen Hook namens useMemo
. Wir werden sie beide besprechen, aber in diesem Stück werden wir uns auf useCallback
als Hauptthema konzentrieren.
Der Hauptunterschied besteht darin, dass useMemo
einen gespeicherten Wert zurückgibt, während useCallback
eine gespeicherte Funktion zurückgibt. Das bedeutet, dass useMemo
zum Speichern eines berechneten Werts verwendet wird, während useCallback
eine Funktion zurückgibt, die Sie später aufrufen können.
Diese Hooks geben Ihnen eine zwischengespeicherte Version zurück, es sei denn, eine ihrer Abhängigkeiten (z. B. Status oder Props) ändert sich.
Werfen wir einen Blick auf die beiden Funktionen in Aktion:
import { useMemo, useCallback } from 'react' const values = [3, 9, 6, 4, 2, 1] // This will always return the same value, a sorted array. Once the values array changes then this will recompute. const memoizedValue = useMemo(() => values.sort(), [values]) // This will give me back a function that can be called later on. It will always return the same result unless the values array is modified. const memoizedFunction = useCallback(() => values.sort(), [values])
Das obige Code-Snippet ist ein erfundenes Beispiel, zeigt aber den Unterschied zwischen den beiden Callbacks:
-
memoizedValue
wird zum Array[1, 2, 3, 4, 6, 9]
. Solange die Variable values bleibt, bleibtmemoizedValue
auch erhalten und wird nie neu berechnet. -
memoizedFunction
ist eine Funktion, die das Array[1, 2, 3, 4, 6, 9]
zurückgibt.
Das Tolle an diesen beiden Rückrufen ist, dass sie zwischengespeichert werden und herumhängen, bis sich das Abhängigkeitsarray ändert. Das bedeutet, dass sie bei einem Rendering keinen Garbage Collection erhalten.
Rendern und Reagieren
Warum ist Merken wichtig, wenn es um React geht?
Es hat damit zu tun, wie React Ihre Komponenten rendert. React verwendet ein im Arbeitsspeicher gespeichertes virtuelles DOM, um Daten zu vergleichen und zu entscheiden, was aktualisiert werden soll.
Das virtuelle DOM hilft React bei der Leistung und hält Ihre Anwendung schnell. Wenn sich ein Wert in Ihrer Komponente ändert, wird die gesamte Komponente standardmäßig neu gerendert. Dadurch reagiert React auf Benutzereingaben und der Bildschirm kann aktualisiert werden, ohne die Seite neu zu laden.
Sie möchten Ihre Komponente nicht rendern, da sich Änderungen nicht auf diese Komponente auswirken. Hier kommt die Memoisierung durch useCallback
und useMemo
ins Spiel.
Wenn React Ihre Komponente neu rendert, erstellt es auch die Funktionen neu, die Sie in Ihrer Komponente deklariert haben.
Beachten Sie, dass beim Vergleich der Gleichheit einer Funktion mit einer anderen Funktion diese immer falsch sind. Da eine Funktion auch ein Objekt ist, entspricht sie nur sich selbst:
// these variables contain the exact same function but they are not equal const hello = () => console.log('Hello Matt') const hello2 = () => console.log('Hello Matt') hello === hello2 // false hello === hello // true
Mit anderen Worten, wenn React Ihre Komponente neu rendert, sieht es alle Funktionen, die in Ihrer Komponente deklariert sind, als neue Funktionen.
Dies ist meistens in Ordnung, und einfache Funktionen sind einfach zu berechnen und wirken sich nicht auf die Leistung aus. Aber in anderen Fällen, in denen Sie nicht möchten, dass die Funktion als neue Funktion gesehen wird, können Sie sich auf useCallback
verlassen, um Ihnen zu helfen.
Sie denken vielleicht: „Wann möchte ich nicht, dass eine Funktion als neue Funktion angesehen wird?“ Nun, es gibt bestimmte Fälle, in denen useCallback
sinnvoller ist:
- Sie übergeben die Funktion an eine andere Komponente, die ebenfalls gespeichert ist (
useMemo
). - Ihre Funktion hat einen internen Zustand, den sie sich merken muss
- Ihre Funktion ist eine Abhängigkeit von einem anderen Hook, wie zum Beispiel
useEffect
Leistungsvorteile von React useCallback
Wenn useCallback
verwendet wird, kann es dazu beitragen, Ihre Anwendung zu beschleunigen und zu verhindern, dass Komponenten erneut gerendert werden, wenn dies nicht erforderlich ist.

Nehmen wir zum Beispiel an, Sie haben eine Komponente, die eine große Datenmenge abruft und für die Anzeige dieser Daten in Form eines Diagramms oder einer Grafik verantwortlich ist, wie hier:

Angenommen, die übergeordnete Komponente für die Komponente Ihrer Datenvisualisierung wird neu gerendert, aber die geänderten Requisiten oder Zustände wirken sich nicht auf diese Komponente aus. In diesem Fall möchten oder müssen Sie es wahrscheinlich nicht erneut rendern und alle Daten erneut abrufen. Durch das Vermeiden dieses erneuten Renderns und erneuten Abrufs kann die Bandbreite Ihres Benutzers eingespart und eine reibungslosere Benutzererfahrung bereitgestellt werden.
Nachteile von React useCallback
Obwohl dieser Haken Ihnen helfen kann, die Leistung zu verbessern, bringt er auch seine Tücken mit sich. Einige Dinge, die Sie vor der Verwendung von useCallback
(und useMemo
) beachten sollten, sind:
- Garbage Collection: Die anderen Funktionen, die noch nicht gespeichert sind, werden von React weggeworfen, um Speicher freizugeben.
- Speicherzuweisung: Ähnlich wie bei der Garbage Collection gilt: Je mehr gespeicherte Funktionen Sie haben, desto mehr Speicher wird benötigt. Außerdem gibt es jedes Mal, wenn Sie diese Rückrufe verwenden, eine Menge Code in React, der noch mehr Speicher verwenden muss, um Ihnen die zwischengespeicherte Ausgabe bereitzustellen.
- Code-Komplexität: Wenn Sie anfangen, Funktionen in diese Hooks einzuschließen, erhöhen Sie sofort die Komplexität Ihres Codes. Es erfordert jetzt ein besseres Verständnis dafür, warum diese Hooks verwendet werden, und eine Bestätigung, dass sie korrekt verwendet werden.
Wenn Sie sich der oben genannten Fallstricke bewusst sind, können Sie sich die Kopfschmerzen ersparen, selbst über sie zu stolpern. Stellen Sie bei der Verwendung useCallback
sicher, dass die Leistungsvorteile die Nachteile überwiegen.
Reagieren useCallback Beispiel
Unten sehen Sie ein einfaches Setup mit einer Button-Komponente und einer Counter-Komponente. Der Counter hat zwei Statusteile und gibt zwei Button-Komponenten aus, die jeweils einen separaten Teil des Status der Counter-Komponenten aktualisieren.
Die Button-Komponente nimmt zwei Requisiten auf: handleClick
und name. Jedes Mal, wenn der Button gerendert wird, wird er bei der Konsole protokolliert.
import { useCallback, useState } from 'react' const Button = ({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> } const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) return ( <> {countOne} {countTwo} <Button handleClick={() => setCountOne(countOne + 1)} name="button1" /> <Button handleClick={() => setCountTwo(countTwo + 1)} name="button1" /> </> ) }
In diesem Beispiel sehen Sie Folgendes in der Konsole, wenn Sie auf eine der beiden Schaltflächen klicken:
// counter rendered // button1 rendered // button2 rendered
Wenn wir jetzt useCallback
auf unsere handleClick
Funktionen anwenden und unseren Button in React.memo
, können wir sehen, was useCallback
uns bietet. React.memo
ähnelt useMemo
und ermöglicht es uns, eine Komponente zu speichern.
import { useCallback, useState } from 'react' const Button = React.memo(({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> }) const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) const memoizedSetCountOne = useCallback(() => setCountOne(countOne + 1), [countOne) const memoizedSetCountTwo = useCallback(() => setCountTwo(countTwo + 1), [countTwo]) return ( <> {countOne} {countTwo} <Button handleClick={memoizedSetCountOne} name="button1" /> <Button handleClick={memoizedSetCountTwo} name="button1" /> </> ) }
Wenn wir jetzt auf eine der Schaltflächen klicken, sehen wir nur die Schaltfläche, auf die wir geklickt haben, um uns bei der Konsole anzumelden:
// counter rendered // button1 rendered // counter rendered // button2 rendered
Wir haben Memoization auf unsere Button-Komponente angewendet, und die Prop-Werte, die ihr übergeben werden, werden als gleich angesehen. Die beiden handleClick
Funktionen werden zwischengespeichert und von React als dieselbe Funktion angesehen, bis sich der Wert eines Elements im Abhängigkeitsarray ändert (z. B. countOne
, countTwo
).
Zusammenfassung
So cool useCallback
und useMemo
sind, denken Sie daran, dass sie bestimmte Anwendungsfälle haben – Sie sollten nicht jede Funktion mit diesen Hooks umschließen. Wenn die Funktion rechnerisch komplex ist, sind eine Abhängigkeit von einem anderen Hook oder eine Prop, die an eine gespeicherte Komponente übergeben wird, gute Indikatoren, die Sie möglicherweise für useCallback
erreichen möchten.
Wir hoffen, dass dieser Artikel Ihnen geholfen hat, diese erweiterte React-Funktionalität zu verstehen und dabei geholfen hat, mehr Vertrauen in die funktionale Programmierung zu gewinnen!