In modernen Webanwendungen sind reaktive Frameworks wie React das Rückgrat für das Handling von Benutzerinteraktionen und der Verwaltung des UI-Zustands. Doch beim Erstellen von interaktiven Komponenten, die große Datenmengen oder komplexe Rendering-Operationen erfordern, kann es zu einer Blockierung der Benutzeroberfläche kommen. Dies führt dazu, dass die gesamte Anwendung während der Berechnung oder der Rendering-Prozesse einfriert und für den Nutzer unbrauchbar wird. React bietet Lösungen wie den Transition Hook, um dieses Problem zu mildern und eine flüssigere Benutzererfahrung zu gewährleisten.
Einführung in die Blockierung der UI
Die Blockierung der UI tritt auf, wenn eine Reaktion auf eine Benutzeraktion, wie z.B. das Laden von Kommentaren, die gesamte Benutzeroberfläche blockiert. Wenn die Rendering-Zeit einer Komponente signifikant wird, wie es oft bei der Anzeige von Tausenden von Kommentaren oder umfangreichen Tabellen der Fall ist, kann der gesamte UI-Thread blockiert werden. Der Benutzer hat dann das Gefühl, dass die Anwendung nicht reagiert, bis der Rendering-Prozess abgeschlossen ist.
Ein typisches Beispiel: Man könnte eine Kommentar-Komponente auf einer Blog-Seite haben, die beim Laden viele Kommentare rendern muss. Wenn man diese Komponente ohne Optimierung direkt im UI-Thread rendert, bleibt der Browser während des Rendering-Prozesses einfrieren. Dies ist nicht nur für den Benutzer frustrierend, sondern kann die Wahrnehmung der Anwendung negativ beeinflussen.
Simulierung der Blockierung der UI
Ein praktisches Beispiel zur Veranschaulichung dieser Problematik ist die Implementierung einer Kommentar-Sektion, die absichtlich langsam gemacht wird, um die Auswirkungen der Blockierung der UI zu demonstrieren. Zuerst wird ein Comment Component erstellt, das absichtlich verzögert wird. In diesem Fall wird die Rendering-Zeit auf eine Millisekunde gesetzt, was eine "kostenintensive" Operation darstellt.
Diese Verzögerung stellt sicher, dass der Rendering-Prozess für die Kommentare lang genug dauert, um die Blockierung des UI sichtbar zu machen. Sobald diese "blockierenden" Komponenten in einer Liste von Kommentaren dargestellt werden, kann der Benutzer die Auswirkungen sehen, da die gesamte Seite unresponsive wird.
Der Übergang zur Lösung: Der Transition Hook
Die Lösung für das Blockierungsproblem, das durch Rendering-intensive Komponenten verursacht wird, ist der Transition Hook. Dieser Hook ermöglicht es, die Darstellung von Komponenten ohne die Blockierung des Haupt-UI-Threads zu steuern. Der useTransition Hook wird wie folgt verwendet:
Mit diesem Hook können Entwickler Operationen als Übergänge markieren, die in den Hintergrund treten, sodass der UI-Thread nicht blockiert wird. Der Übergang läuft asynchron und lässt die UI weiterhin reagieren, auch wenn die Daten im Hintergrund verarbeitet werden. Wenn der Übergang läuft, bleibt der Zustand der UI und deren Interaktivität erhalten, was zu einer besseren Benutzererfahrung führt.
In unserem Kommentar-Beispiel können wir den useTransition Hook nutzen, um die Anzeige von Kommentaren zu optimieren, sodass die Interaktionen mit der Seite flüssiger und ohne Blockierungen erfolgen.
Beispiel: Integration des Transition Hooks
Um die Blockierung zu vermeiden, integrieren wir den Transition Hook in das Kommentar-Rendering. Im Folgenden sehen wir den aktualisierten Code, der es uns ermöglicht, die Anzeige von Kommentaren asynchron zu steuern, ohne den gesamten UI-Thread zu blockieren.
In diesem Beispiel haben wir den Zustand showComments innerhalb einer Transition verändert. Dies bedeutet, dass das Umschalten der Anzeige von Kommentaren nun nicht mehr den gesamten UI-Thread blockiert. Stattdessen wird es im Hintergrund ausgeführt, was sicherstellt, dass die restlichen UI-Elemente weiterhin interaktiv bleiben.
Zusätzliche Hinweise zur Verwendung von Transitions
Obwohl der Transition Hook eine leistungsstarke Möglichkeit bietet, UI-Blockierungen zu vermeiden, gibt es bestimmte Einschränkungen, die beachtet werden sollten. Zum Beispiel sollten Transitions nicht für die Handhabung von kontrollierten Eingaben verwendet werden, da bei Eingabefeldern eine sofortige Reaktion erforderlich ist, die durch Transitions verzögert werden könnte. Ebenso sollten alle Statusänderungen innerhalb eines Transitions-Callbacks sofort durchgeführt werden, ohne dass auf asynchrone Vorgänge gewartet wird.
Wenn die Anwendung eine aufwendige Berechnung durchführen muss, aber die UI dennoch nicht blockiert werden soll, ist der Transition Hook die ideale Wahl. Es ist jedoch wichtig, Transitions in den richtigen Kontexten zu verwenden, da sie nicht für alle Arten von Zustandsaktualisierungen geeignet sind.
Fazit
Die Verwendung des Transition Hooks in React ermöglicht eine bedeutende Verbesserung der Benutzererfahrung, indem sie sicherstellt, dass Rendering-intensive Operationen wie das Anzeigen von Kommentaren nicht mehr die gesamte Anwendung blockieren. Entwickler sollten jedoch auch die spezifischen Anwendungsfälle und Einschränkungen dieses Hooks im Auge behalten, um sicherzustellen, dass er effizient und korrekt eingesetzt wird.
Neben der Verwendung des Transition Hooks ist es auch wichtig, den Zustand der Anwendung regelmäßig zu überprüfen und gegebenenfalls andere Optimierungen wie Code-Splitting oder Lazy Loading zu implementieren, um die Gesamtleistung zu verbessern und die Benutzererfahrung zu maximieren.
Wie baut man eigene React-Hooks?
React bietet eine große Flexibilität bei der Entwicklung von Anwendungen, und eine der mächtigsten Funktionen sind die sogenannten "Hooks". Diese ermöglichen es Entwicklern, State und andere React-Funktionen in Funktionskomponenten zu verwenden. Während die vorgefertigten Hooks wie useState, useEffect und useContext in den meisten Anwendungen ausreichen, gibt es Szenarien, in denen die Erstellung eigener Hooks erforderlich ist. Die Möglichkeit, benutzerdefinierte Hooks zu erstellen, ist ein wesentliches Werkzeug, um die Wartbarkeit und Wiederverwendbarkeit des Codes in größeren Projekten zu verbessern. In dieser Hinsicht ist es wichtig zu verstehen, wie man eigene Hooks erstellt und wann dies sinnvoll ist.
Ein benutzerdefinierter Hook ist einfach eine Funktion, die ein oder mehrere React-Hooks verwendet und zusätzliche Logik kapselt. Dies kann besonders hilfreich sein, wenn ähnliche Logik in mehreren Komponenten benötigt wird, beispielsweise bei der Verwaltung von API-Anfragen, Zustandsänderungen oder komplexeren Geschäftslogiken. Durch die Kapselung dieser Logik in einem Hook können Entwickler den Code besser organisieren und redundante Logik vermeiden. Diese Technik sorgt nicht nur für eine klarere Struktur, sondern erleichtert auch das Testen und die Fehlerbehebung.
Die Erstellung benutzerdefinierter Hooks beginnt in der Regel mit einer sorgfältigen Analyse der Anforderungen und der Identifikation von wiederverwendbaren Logik-Teilen. Ein Beispiel für einen benutzerdefinierten Hook könnte ein useFetch-Hook sein, der die Logik zur Durchführung einer API-Anfrage übernimmt. Dies könnte folgendermaßen aussehen:
Dieser Hook kapselt die Logik für das Abrufen von Daten und den Zustand des Ladens oder Fehlers, sodass jede Komponente, die ihn verwendet, diese Informationen auf einfache Weise abrufen kann. Die Verwendung dieses Hooks in einer Komponente könnte folgendermaßen aussehen:
Ein weiterer wichtiger benutzerdefinierter Hook könnte ein Zustandshandler für Formulare sein. Dieser könnte den Zustand und die Validierung der Formulardaten verwalten, wodurch die Logik für die Formularbehandlung in einer einzigen Funktion gebündelt wird, die von verschiedenen Komponenten wiederverwendet werden kann.
Neben der Erstellung einfacher benutzerdefinierter Hooks können auch komplexere Hooks entwickelt werden, die mehrere interne Zustände oder Effekte kombinieren. Ein Beispiel hierfür ist ein Debounced History State Hook, der verwendet wird, um eine Zustandsänderung erst nach einer Verzögerung zu speichern. Dies kann nützlich sein, wenn man den Zustand von Benutzereingaben verfolgen möchte, ohne dass bei jeder Eingabe eine sofortige Aktualisierung der Geschichte erfolgt.
Ein solches Hook könnte folgendermaßen aussehen:
Dieser Hook speichert den aktuellen Zustand nach einer festgelegten Verzögerung und kann für verschiedene Zwecke eingesetzt werden, z. B. bei der Implementierung einer Autovervollständigung oder der Verarbeitung von Benutzereingaben in Echtzeit.
Es ist wichtig, bei der Erstellung eigener Hooks die Prinzipien der Wiederverwendbarkeit und der sauberen Trennung der Logik zu berücksichtigen. Ein benutzerdefinierter Hook sollte so generisch wie möglich sein, sodass er in verschiedenen Komponenten und Szenarien verwendet werden kann, ohne dass unnötige Abhängigkeiten oder spezifische Logik eingebaut werden.
Für Entwickler, die neu in der Entwicklung von benutzerdefinierten Hooks sind, kann es zunächst schwierig erscheinen, wann und wie diese eingesetzt werden sollten. Ein häufiges Missverständnis ist, dass man benutzerdefinierte Hooks für jede Kleinigkeit erstellen muss. Oft reicht es, sich auf die wichtigen wiederverwendbaren Logikbereiche zu konzentrieren und diese in Hooks auszulagern, die gut dokumentiert und getestet sind.
Zusätzlich ist es sinnvoll, bei der Erstellung von Hooks auch die Testbarkeit im Auge zu behalten. Insbesondere in größeren Projekten mit vielen benutzerdefinierten Hooks ist es wichtig, sicherzustellen, dass die Logik korrekt funktioniert und keine unerwünschten Nebeneffekte auftreten. React bietet dafür hervorragende Tools, wie die React Testing Library, mit denen Entwickler Hooks testen und sicherstellen können, dass sie die erwartete Funktionalität bieten.
Es sollte auch betont werden, dass benutzerdefinierte Hooks nicht nur für die Zustandsverwaltung nützlich sind. Sie können auch verwendet werden, um die Kommunikation mit externen APIs zu abstrahieren, Anwendungszustände wie das Laden oder Fehler zu verwalten oder wiederverwendbare UI-Komponenten zu erstellen, die immer wieder verwendet werden können.
Insgesamt bieten benutzerdefinierte Hooks eine mächtige Möglichkeit, die Codebasis zu strukturieren, die Wiederverwendbarkeit zu fördern und die Wartbarkeit von React-Anwendungen zu erhöhen. Entwickler sollten sich mit dieser Technik vertraut machen, um den vollen Nutzen aus React zu ziehen und eine effiziente und skalierbare Entwicklung zu gewährleisten.
Wann sollten wir den Memo- und Callback-Hook in React verwenden?
In der Welt von React werden Hooks als Werkzeuge verwendet, um die Funktionsweise von Komponenten zu optimieren und bestimmte Probleme in der Anwendungsentwicklung zu lösen. Zwei solche Hooks, die oft im Zusammenhang mit Leistungsoptimierungen erwähnt werden, sind der useMemo- und der useCallback-Hook. Beide sind mächtige Werkzeuge, die helfen können, die Leistung von React-Anwendungen zu steigern, indem sie unnötige Berechnungen und Renderzyklen vermeiden. Doch wie und wann sollten diese Hooks sinnvoll eingesetzt werden?
Der useMemo-Hook ist dafür zuständig, Werte zu speichern und nur dann neu zu berechnen, wenn sich die Abhängigkeiten des Werts ändern. Dies ist besonders nützlich, wenn teure Berechnungen in einer Komponente stattfinden, deren Ergebnisse nicht jedes Mal neu berechnet werden müssen, wenn die Komponente gerendert wird. Ein Beispiel: Wenn eine Komponente eine Liste von Items filtert, sortiert oder auf eine andere Weise verarbeitet, kann es sinnvoll sein, den gefilterten oder sortierten Wert in einem useMemo-Hook zu speichern. Auf diese Weise wird diese Berechnung nur dann erneut durchgeführt, wenn sich die Eingabewerte ändern, was die Gesamtleistung der Anwendung verbessert. Der Hook wird jedoch nur dann verwendet, wenn ein tatsächliches Leistungsproblem vorliegt – das heißt, wenn die React-Compiler-Optimierungen nicht ausreichen, um die gewünschte Leistung zu erzielen. Ohne ein solches Problem kann der Einsatz von useMemo unnötig und sogar kontraproduktiv sein, da React intern bereits eine effiziente Verwaltung der Renderzyklen bietet.
Der useCallback-Hook funktioniert ähnlich wie useMemo, jedoch mit einem Fokus auf Funktionen. Auch hier geht es darum, eine Funktion nur dann neu zu definieren, wenn sich eine ihrer Abhängigkeiten ändert. In React-Anwendungen können Funktionen oft als Props an untergeordnete Komponenten übergeben werden. Wenn eine Funktion bei jedem Rendern neu erstellt wird, kann dies zu unnötigen Neurendern der untergeordneten Komponenten führen, da React davon ausgeht, dass sich die übergebene Funktion geändert hat. Der useCallback-Hook stellt sicher, dass diese Funktion nur dann neu erstellt wird, wenn sich tatsächlich eine der Abhängigkeiten der Funktion ändert. Ein Beispiel wäre eine Funktion, die als Callback für einen Button-Click in einer untergeordneten Komponente dient – wenn die Funktion bei jedem Rendern neu erstellt wird, kann dies unnötige Renders in der gesamten Komponentenhierarchie auslösen. Durch die Verwendung von useCallback wird die Funktion nur dann neu erstellt, wenn es notwendig ist, was die Leistung verbessert.
Es gibt jedoch einen wichtigen Punkt zu beachten: Diese Hooks sollten nicht in jedem Fall verwendet werden. Sie sind nur dann sinnvoll, wenn es ein erkennbares Leistungsproblem gibt. Das bedeutet, dass sie in der Regel nur dann verwendet werden sollten, wenn man auf wiederholte und teure Berechnungen oder Renderzyklen stößt, die die Benutzererfahrung beeinträchtigen. In den meisten Fällen wird der React-Compiler eine sehr gute Leistung bieten, ohne dass diese speziellen Optimierungen notwendig sind.
Zusätzlich zu den Grundlagen des useMemo- und useCallback-Hooks gibt es auch fortgeschrittene Hook-Varianten, die in speziellen Fällen hilfreich sein können. Beispielsweise gibt es den useLayoutEffect-Hook, der ähnlich wie useEffect ist, aber synchron ausgeführt wird, nachdem alle DOM-Manipulationen abgeschlossen sind. Dies ist besonders nützlich, wenn Sie den DOM direkt manipulieren oder das Layout der Seite vor dem Rendering anpassen müssen. Der useInsertionEffect-Hook ist eine noch speziellere Variante, die vor den Layout-Effekten ausgeführt wird und hauptsächlich für die Verwendung in CSS-in-JS-Bibliotheken gedacht ist. Diese fortgeschrittenen Hooks bieten noch mehr Kontrolle über den Rendering-Prozess, sollten aber nur in wirklich speziellen Anwendungsfällen eingesetzt werden, da sie zu einer Verlangsamung der Anwendung führen können, wenn sie unnötig verwendet werden.
Es ist auch wichtig zu wissen, dass es im React-Ökosystem eine Vielzahl weiterer Hooks gibt, die von der Community entwickelt wurden. Diese können für spezifische Anwendungsfälle, wie z.B. das Verwalten von Zustand in der Anwendung oder die Implementierung von Debouncing, sehr nützlich sein. Einige dieser Hooks, wie der useLocalStorage-Hook, erlauben es, Daten im lokalen Speicher des Browsers zu speichern, was für persistenten Zustand oder Authentifizierungs-Token nützlich sein kann.
Die wichtigsten Erkenntnisse über die Verwendung von Memo- und Callback-Hooks sind:
-
Diese Hooks sind leistungsoptimierende Werkzeuge, die dann zum Einsatz kommen sollten, wenn es ein echtes Leistungsproblem gibt, das durch Reacts eingebaute Mechanismen nicht zufriedenstellend gelöst wird.
-
Der
useMemo-Hook hilft, teure Berechnungen zu optimieren, indem er deren Wiederholung vermeidet. -
Der
useCallback-Hook verhindert unnötige Neudefinitionen von Funktionen, die als Props weitergegeben werden, und optimiert damit das Renderverhalten von Komponenten. -
Beide Hooks sollten nicht in jedem Fall verwendet werden, sondern nur, wenn sie nachweislich die Performance verbessern.
Diese Optimierungen können für komplexe Anwendungen oder Anwendungen mit vielen Interaktionen und zustandsabhängigen Berechnungen entscheidend sein. Sie bieten Entwicklern eine Möglichkeit, die Leistung zu steigern und die Benutzererfahrung zu verbessern, indem sie die Rendering-Zyklen effizienter gestalten.
Wie man ein Projekt mit Vite und React einrichtet und Best Practices umsetzt
Nachdem Sie den Ordner aus den bereitgestellten GitHub-Codebeispielen geklont haben, wird eine Benachrichtigung angezeigt, die darauf hinweist, dass ein Git-Repository gefunden wurde. Diese können Sie einfach schließen, da wir nur den Ordner „Chapter01_1“ öffnen möchten. Sobald Visual Studio Code (VS Code) bereit ist, können wir fortfahren und ein neues Projekt mit Vite einrichten.
Vite ist eine der beliebtesten Optionen für die lokale Entwicklung und ermöglicht die schnelle Einrichtung eines modernen Frontend-Projekts. Laut der Umfrage „The State of JS 2024“ ist Vite besonders bei Entwicklern geschätzt, da es eine herausragende Leistung bietet und eine unkomplizierte Konfiguration ermöglicht. In diesem Kapitel richten wir unser Projekt mit Vite ein, da es eine schnelle und einfache Möglichkeit bietet, eine moderne Entwicklungsumgebung aufzubauen.
Um Ihr Projekt mit Vite einzurichten, gehen Sie folgendermaßen vor:
-
Öffnen Sie das Terminal in VS Code über die Menüleiste „Terminal | Neues Terminal“.
-
Geben Sie im Terminal den folgenden Befehl ein:
$ npm create [email protected] .(das.am Ende sorgt dafür, dass das Projekt im aktuellen Ordner erstellt wird). Bestätigen Sie mit Enter. -
Wenn Sie gefragt werden, ob „create-vite“ installiert werden soll, tippen Sie „y“ und drücken Sie erneut Enter.
-
Sollten Sie gefragt werden, ob der aktuelle Ordner leer ist, wählen Sie die Option „Remove existing files and continue“, um bestehende Dateien zu entfernen und fortzufahren.
-
Wenn Sie nach dem Paketnamen gefragt werden, bestätigen Sie einfach den Standardnamen, indem Sie Enter drücken.
-
Wählen Sie „React“ als Framework aus, wenn Sie dazu aufgefordert werden, und drücken Sie Enter.
-
Wählen Sie JavaScript als Variante, da in diesem Buch nur plain JavaScript verwendet wird.
-
Überprüfen Sie die Datei
package.json, um sicherzustellen, dass die Versionen der Abhängigkeiten mit den folgenden übereinstimmen: -
Führen Sie im Terminal den Befehl
npm installaus, um alle Abhängigkeiten zu installieren. -
Starten Sie den Entwicklungsserver mit
npm run devund bestätigen Sie, dass alles funktioniert, indem Sie die Anwendung im Browser aufrufen. Der Server sollte Ihnen eine URL anzeigen, unter der die App läuft. Öffnen Sie diese URL in Ihrem Browser.
Nun haben wir ein einfaches Projekt mit Vite und React eingerichtet, das auf lokale Entwicklungsänderungen reagiert. Um das Projekt weiter zu testen, klicken Sie auf die Schaltfläche „count is 0“, und der Wert sollte bei jedem Klick erhöht werden.
Alternativen zu Vite
Es gibt mehrere Alternativen zu Vite, wie z. B. Webpack, Rollup und Parcel. Diese Tools bieten umfangreiche Konfigurationsmöglichkeiten, jedoch erfordert ihre Nutzung häufig einen komplexeren Setup-Prozess und ist weniger optimiert für die Entwicklungsumgebung, da sie zuerst den gesamten Code bündeln müssen, bevor er im Browser ausgeführt wird. Vite hingegen unterstützt das ECMAScript-Modul-Format (ESM) nativ und benötigt nur wenig Konfiguration, um loszulegen.
Ein neuerer Bundler, der vielversprechend ist, ist Rolldown, aber zu diesem Zeitpunkt noch in einer frühen Entwicklungsphase.
Bestehende Best Practices durch ESLint und Prettier umsetzen
Nun, da das Projekt eingerichtet ist, ist es wichtig, Tools wie ESLint und Prettier zu integrieren, die für eine konsistente Codequalität sorgen und Best Practices durchsetzen. ESLint hilft dabei, gängige Fehler und unsaubere Codierungspraktiken zu vermeiden, während Prettier dafür sorgt, dass der Code immer im gleichen Stil formatiert wird, was gerade in Teams von großer Bedeutung ist.
Installation der erforderlichen Abhängigkeiten
Um Prettier und ESLint zu installieren, öffnen Sie ein weiteres Terminalfenster in VS Code und geben Sie folgenden Befehl ein:
Dadurch wird Prettier und die ESLint-Konfiguration für Prettier als Entwicklungsabhängigkeiten hinzugefügt.
Konfiguration von Prettier
Erstellen Sie eine neue Datei namens .prettierrc.json im Projektordner, um Prettier zu konfigurieren. Hier ein Beispiel für eine Konfigurationsdatei:
Diese Konfiguration sorgt dafür, dass am Ende von Arrays und Objekten immer ein Komma gesetzt wird, was besonders bei größeren Änderungen nützlich ist, da es die Zahl der Zeilen reduziert, die Git als verändert anzeigt. Auch wird der Code standardmäßig mit 2 Leerzeichen eingerückt, und lange Zeilen werden auf 80 Zeichen begrenzt.
Fazit
Die Einrichtung eines Projekts mit Vite und die Integration von ESLint sowie Prettier sind wichtige Schritte, um sicherzustellen, dass Ihr Code sowohl schnell als auch sauber bleibt. Durch Vite erhalten Sie ein modernes, leistungsfähiges Setup mit wenig Overhead, während ESLint und Prettier dazu beitragen, dass Ihr Code klar und konsistent bleibt. Es lohnt sich, diese Tools von Anfang an in Ihr Projekt zu integrieren, um eine solide Basis für die weitere Entwicklung zu schaffen.

Deutsch
Francais
Nederlands
Svenska
Norsk
Dansk
Suomi
Espanol
Italiano
Portugues
Magyar
Polski
Cestina
Русский