Angular
HTML & CSS
JavaScript
Vue & Svelte

React 19 – Die neuesten Features

3. Sep 2024

2024 ist das Jahr von React 19. Warum ist das so besonders? Weil das Team hinter der populären Bibliothek ein ziemliches Geheimnis um das nächste Release macht. Das neueste React-Release verspricht Verbesserungen sowohl an den APIs von React als auch an der Developer Experience sowie an der Performance von Applikationen. Was genau in React 19 auf Sie wartet, und ob sich das Update lohnt, erfahren Sie in diesem Artikel.

Das React-Team setzt auf eine ganz unterschiedliche Strategie als andere Projekte, wie beispielsweise Angular oder Node.js mit ihren festen Releasezyklen. Doch warum ist ein solch starrer Releasezyklus erstrebenswert? Setzen Sie Bibliotheken oder Frameworks in Ihrer Applikation ein, können Sie mit einem festen Releasezyklus Aktualisierungen planen.

Der große Vorteil von React ist, dass sich Breaking Changes in der Regel trotz Major-Releases im Rahmen halten. Ein Breaking Change ist die Änderung einer Funktionalität, die dafür sorgt, dass die verwendete Software nicht mehr kompatibel ist. In diesem Fall müssen Sie Ihren Quellcode an die neue Version anpassen. Damit Sie dadurch keinem unnötigen Risiko in der Entwicklung Ihrer Applikation ausgesetzt sind, arbeiten die Entwickler:innen von Bibliotheken und Frameworks mit verschiedenen Strategien. Features, die in zukünftigen Versionen nicht mehr vorhanden sein werden, werden als deprecated markiert, während Features, die neu entwickelt werden, als experimentelle Features bezeichnet werden. Diese Features sind meist als Vorschau gedacht – Entwickler:innen können diese Features testen und Feedback geben. Deshalb ist es auch üblich, dass sich die APIs dieser Features auch noch ändern. Experimentelle Features sind also nicht unbedingt für den Produktiveinsatz geeignet. Doch was heißt das jetzt alles für die neue Version von React?

Die neuen Features von React kümmern sich darum, zum einen die Developer Experience und zum anderen die Performance zu verbessern.

Die React-Releases

Die jüngere Geschichte von React reicht zurück bis in den September 2017 mit dem Release der Version 16. Ein bahnbrechendes Release, da das Team den Reconciler, den Kern von React, von Grund auf neu entwickelte. Dieses Release machte den Weg für das Hook API frei. Das Hook API sorgte dafür, dass React-Komponenten nicht mehr wie bisher vor allem mit Klassen oder für die einfacheren Komponenten aus Funktionen, sondern nur noch mit Funktionen umgesetzt wurden. Neben diesem Paradigmenwechsel kamen noch weitere Features wie Suspense oder die Server Components hinzu. Seit der Version 16 gab es alle zwei bis drei Jahre ein neues Major-Update:

  • Version 17 (Oktober 2020): Im Gegensatz zu den Versionen des 16er-Zweigs gab es mit dieser Version keine nennenswerten größeren Features. Die Modernisierungen haben sich mehr auf die internen Strukturen bezogen und den Weg für zukünftige Verbesserungen vorbereitet.

  • Version 18 (März 2022): In diesem Release gab es wieder zahlreiche Neuerungen und Verbesserungen wie den Concurrent Mode und eine neue Rendering Engine, die die parallele Abarbeitung von Aufgaben erlaubt. Außerdem wurde das Suspense-Feature und die serverseitigen Schnittstellen überarbeitet und verbessert. Mit diesem Release zeigt sich immer mehr, dass React nicht nur im Frontend zuhause ist, sondern sich immer mehr in Richtung Fullstack-Entwicklung bewegt.

  • Version 19 Beta (April 2024): Das neueste Release verspricht wieder eine ganze Reihe von Verbesserungen sowohl an den APIs von React als auch an der Performance von Applikationen. Das React-Team nimmt auch immer das Feedback der Community auf und sorgt dafür, dass React eine hilfreiche Bibliothek für moderne Webapplikationen bleibt.

Installation der neuesten Version

Üblicherweise installiert man React wie so ziemlich jede JavaScript-Bibliothek als Paket über npm. Geben Sie keine Versionsnummer an, erhalten Sie die aktuelle stabile Version. Das gilt auch für React. Gerade, wenn es um die neueren Features geht, ist die stabile Version die falsche Wahl. Das React-Team stellt noch zwei weitere Versionen zur Verfügung:

  • Experimental: Diese Version enthält experimentelle Features. Sie ist eine Art Spielwiese für neue Features. Die Features sind nicht garantiert stabil und die Version ist nicht für den Produktiveinsatz geeignet.

  • Canary: Die Canary-Version ist ein Pre-Release, mit dem Sie, aber auch Bibliotheksautoren, die neuesten Features von React testen können. Die Canary-Version ist deutlich stabiler als die Experimental-Version.

Um also in den Genuss der neuesten Features zu kommen, erstellen Sie eine neue React-Applikation mit der aktuellen stabilen Version, beispielsweise mit Vite. Auf dieser Basis können Sie dann auf die Canary-Version aktualisieren, indem Sie die Pakete react@canary und react-dom@canary installieren. Diese Vorgehensweise ist natürlich nur relevant, solange Version 19 noch nicht als stabile Version geführt wird.

Das React-Team hat anlässlich der Ankündigung von Version 19 sowohl über Social Media als auch auf seinem Blog vorgestellt, woran sie gerade arbeiten und welche Features in die neue Version einfließen werden. Und auf genau diese Features werden wir im Folgenden einen genaueren Blick werfen.

 

Der React-Compiler

React bekommt einen Compiler. Dass Sie den Code einer React-Applikation übersetzen lassen müssen, um ihn im Browser auszuführen, ist nichts neues. Denn, wenn Sie versuchen, den Code einer React-Komponente mit JSX direkt im Browser laufen zu lassen, reagiert dieser mit einem Syntaxfehler. React-Entwickler:innen sind also grundsätzlich an Übersetzungsprozesse gewöhnt. Der React-Compiler schlägt aber ein ganz neues Kapitel in der Entwicklung auf. Er ist, zumindest momentan, nicht direkt Bestandteil von React, sondern ein eigenständiges Parallelprojekt. Er erfordert jedoch Version 19, um funktionieren zu können. Es gibt noch einige weitere Rahmenbedingungen, die Ihr Code erfüllen muss, damit der Compiler seine Arbeit erfüllen kann. Doch zunächst sollten wir die Frage klären: Warum brauchen wir ein weiteres Werkzeug für React?

Der Compiler optimiert den Code Ihrer Applikation zur Build-Zeit. Die wichtigste Aufgabe des Compilers ist die Memoisierung von Strukturen. Das Thema Memoisierung gibt es in React schon deutlich länger als die Überlegung für den Compiler. Die folgenden Funktionen können Sie bereits jetzt für die Memoisierung von Komponenten, Funktionen und Objekten verwenden:

  • memo: Mit dieser Funktion können Sie Komponenten memoisieren. Die Komponenten, die Sie dieser Funktion übergeben, rendert React nur neu, wenn sich ihre Props ändern. Dazu vergleicht React die Prop-Werte zwischen den Render-Zyklen. Sie können die Standardvergleichsfunktion durch Ihre eigene Implementierung ersetzen. Allerdings besitzt die Memoisierung mit der memo-Funktion für React lediglich einen Vorschlagscharakter. Die Bibliothek entscheidet in letzter Instanz immer selbst, ob eine Komponente neu gerendert wird. Sie sollten memo also nur zur Verbesserung der Performance und nicht zum Steuern von Render-Zyklen verwenden.

  • useMemo: Die memo-Funktion arbeitet mit ganzen Komponenten. Wollen Sie innerhalb einer Komponente den Wert einer Berechnung speichern, sodass er beim Neurendern nicht wieder berechnet werden muss, können Sie useMemo verwenden. Dieser Funktion übergeben Sie eine Callback-Funktion, die einen Wert berechnet, und ein Array von Abhängigkeiten. Sobald sich eine der Abhängigkeiten ändert, wird der Wert erneut berechnet, ansonsten arbeitet React mit dem zwischengespeicherten Wert.

  • useCallback: In React arbeiten Sie sehr häufig mit Callback-Funktionen. Auch diese können Sie mit useMemo memoisieren, damit die Funktionsobjekte nicht bei jedem Render-Zyklus neu erzeugt werden. useCallback ist eine Hilfsfunktion, deren Funktionalität Sie mit etwas mehr Aufwand auch mit useMemo nachbilden können.

Der React-Compiler macht diese Funktionen überflüssig und erlaubt Ihnen vor allem, sich keine Gedanken mehr machen zu müssen, wann Sie die Funktionen einsetzen und wann Sie sie besser vermeiden sollten. Generell erschweren die Memoisierungsfunktionen die Lesbarkeit des Codes. Fällt das weg, ist es auf jeden Fall ein Gewinn für Lesbarkeit und Performance. Der React-Compiler wurde während der Entwicklung auch als React-Forget-Compiler bezeichnet, da Sie alles, was Sie bisher über Memoisierung wissen mussten, mit der Einführung des Compilers vergessen können.

Rules of React

Damit der Compiler seine Arbeit verrichten kann, muss Ihr Code einige Anforderungen erfüllen. Dafür gibt es die Rules of React. Wenn Ihnen das von den Rules of Hooks bekannt vorkommt, kommt das nicht von ungefähr – die Rules of Hooks sind Bestandteil der Rules of React. Auch wenn Sie den Compiler nicht verwenden möchten, sollten Sie sich dennoch an die Rules of React halten. Diese Regeln sind im Einzelnen:

  • Pure Komponenten und Hooks: Diese Regeln sorgen dafür, dass der Code verständlich, einfach zu debuggen und vor allem gut zu optimieren ist.

    • Komponenten müssen idempotent sein: Bei gleichen Props, State und Context liefern Komponenten das gleiche Resultat.

    • Seiteneffekte müssen außerhalb des Renderns ausgeführt werden: Seiteneffekte dürfen nicht direkt in der Komponentenfunktion gestartet werden, sondern müssen in useEffect gekapselt werden.

    • Props und State sind immutable: Props und State sollten niemals manuell verändert werden.

    • Rückgabewerte und Argumente von Hooks sind immutable: Was für Props und State einer Komponente gilt, gilt auch für Rückgabewerte und Argumente von Hooks.

    • Werte sind immutable, sobald sie an JSX übergeben werden.

  • React ruft Komponenten und Hooks auf: Der deklarative Ansatz sieht vor, dass Sie React das gewünschte Ergebnis beschreiben. Den Aufruf der zugehörigen Funktionen übernimmt React selbst.

    • Komponentenfunktionen werden nicht direkt aufgerufen.

    • Hook-Funktionen werden nicht als reguläre Werte übergeben.

  • Rules of Hooks

    • Hooks werden nur auf der obersten Ebene aufgerufen.

    • Hooks werden nur in React-Funktionen aufgerufen.

Viele Regeln erschließen sich von selbst, wenn Sie bedenken, dass React stark auf Immutable Values setzt. Sie sollten sich also generell angewöhnen, dass Sie Werte nicht modifizieren, sondern erst kopieren und dann mit der Kopie arbeiten. Ob Sie nun also mit den neuen Features von React 19 experimentieren oder an einer ganz gewöhnlichen Applikation arbeiten, Sie sollten in jedem Fall versuchen, diese Regeln zu beherzigen.

Installation und Verwendung des Compilers

Vor der Verwendung des Compilers empfiehlt das React-Team einen Health-Check. Zu diesem Zweck gibt es das Paket react-compiler-healthcheck, das Sie mit dem Kommando npx react-compiler-healthcheck in Ihrer Applikation ausführen können. Dieser Health-Check kompiliert Ihre Komponenten und stellt sicher, dass hierbei keine Probleme entstehen. Außerdem überprüft er, ob Sie den StrictMode in Ihrer Applikation aktiviert haben und keine inkompatiblen Bibliotheken verwenden. Ein Beispiel für eine solche Bibliothek ist MobX, da hier Proxys verwendet werden, die ein Problem für den Compiler bedeuten.

Zusätzlich zum Health-Check gibt es ein ESLint-Plug-in, mit dem Ihnen ESLint beziehungsweise Ihre Entwicklungsumgebung bei der Umsetzung der Rules of React helfen kann, indem es Ihnen Compilerfehler direkt anzeigt.

Nachdem Sie den Health-Check durchgeführt haben und die Ausgabe keinen Fehler ergeben hat, können Sie sich daran machen, den Compiler in Ihre Applikation zu integrieren. Das funktioniert in den verschiedensten Umgebungen wie in einer Vite-Applikation, in Next.js, aber auch mit webpack. Die Dokumentation des Compilers gibt Ihnen eine Schritt-für-Schritt-Anleitung für jede mögliche Umgebung. Nutzen Sie beispielsweise Vite, müssen Sie mit dem Kommando npm install -D babel-plugin-react-compiler das Compiler-Plug-in installieren und die Vite-Konfiguration wie in Listing 1 anpassen.

Listing 1: Integration des Compilers in die Vite-Konfiguration

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

const ReactCompilerConfig = {
  /* ... */
};

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react({
      babel: { plugins: ['babel-plugin-react-compiler', ReactCompilerConfig] },
    }),
  ],
});
 

Listing 2: Komponente mit State und Kindkomponenten

import { useState } from 'react';
import Button from './Button';
import Child from './Child';

const Counter: React.FC = () => {
  const [counter, setCounter] = useState(0);

  return (
    <div>
      {counter}
      <Button onClick={() => setCounter((prevCounter) => prevCounter + 1)} />
      <Child />
    </div>
  );
};

export default Counter;
 

Damit Sie sehen, was der Compiler für Sie tun kann, gehen wir von der Komponente in Listing 2 aus. Diese Komponente weist einen lokalen State auf, in dem Sie eine Zahl verwalten. Außerdem hat die Komponente zwei Kindkomponenten: eine Button-Komponente und eine Child-Komponente. Die Button-Komponente rendert einen Button, auf dessen Click Event der State um eins hochgezählt wird. Die Child-Komponente ist eine reine Anzeigekomponente und hat keine Verbindung zum State der Elternkomponente. Betätigen Sie den Button in einer Applikation ohne Compiler, rendert React die Elternkomponente und beide Kindkomponenten, und zwar bei jedem Klick auf den Button.

Aktivieren Sie den Compiler, sieht die Situation ganz anders aus. Zur Build-Zeit erzeugt der Compiler einen separaten Memocache im Code. Mit dieser Struktur kann React zur Laufzeit sicherstellen, dass nur die Komponenten neu gerendert werden, deren Inhalt sich tatsächlich geändert hat. Klicken Sie dann auf den Button, wird nur die Elternkomponente neu gerendert, nicht jedoch die beiden Kindkomponenten. Dem Vorteil der Laufzeitperformance steht eine etwas größere Bundle Size und ein etwas länger laufender Build-Prozess entgegen. Die Erklärung hierfür liegt jedoch auf der Hand: Der Compiler muss den Code analysieren, was Zeit in Anspruch nimmt, und die Cachestruktur aufbauen, was sich auf die Bundle Size auswirkt.

Weitere Neuerungen mit React 19

Der Compiler, eine der spannendsten Neuerungen in React, ist eigentlich überhaupt kein Feature von React 19, sondern wird parallel dazu entwickelt und baut nur auf der neuen Version auf. React selbst bringt aber auch ein paar spannende Neuerungen mit.

Directives

Die neue Version von React führt zwei sogenannte Direktiven ein. Sie heißen use client und use server. Falls Sie schon mit Next.js gearbeitet haben, sollten Ihnen diese beiden Zeichenketten bekannt vorkommen. Sie markieren den Übergang zwischen Client und Server und sind für die Verwendung von React in einer Full-Stack-Umgebung, wie beispielsweise Next.js, gedacht. Die Zeichenkette muss vor dem gesamten Code einer Datei kommen, auch vor den Import-Statements. Diese beiden Direktiven zeigen, dass das React-Team den Schritt in Richtung Full-Stack-Entwicklung mit React wirklich ernst meint und Frameworks, die mit React Server Components arbeiten, wie beispielsweise Next.js, unterstützen.

Document Metadata

React 19 fügt die Unterstützung für die Elemente <title><meta> und <link> hinzu. Wie alle Elemente rendert React auch diese in ihre DOM-Entsprechungen. Die Besonderheit bei diesen Elementen ist, dass es sich dabei um Metaelemente handelt, also HTML-Tags, die der Browser nicht für die Anzeige von Inhalten verwendet, sondern die Sie nutzen können, um Informationen für den Browser, Suchmaschinen oder andere Dienste zu definieren. Mit der Unterstützung der Metaelemente trägt React der Tatsache Rechnung, dass Suchmaschinenoptimierung eine immer größere Rolle in der Entwicklung von Applikationen spielt und diese solche Metaelemente benötigt.

React behandelt die Metaelemente anders als die üblichen Standardelemente und rendert sie im head-Bereich des Dokuments und nicht an der Stelle, an der Sie sie in der Komponente angegeben haben. Die Bibliothek kümmert sich dabei nicht darum, dass die Elemente eindeutig sind. Das kann beispielsweise dazu führen, dass Ihr aktuelles Dokument über mehr als nur ein title-Tag verfügt. Der Browser kann allerdings damit umgehen und nutzt den zuletzt eingefügten Tag. Das bedeutet jedoch, dass Sie ein gewisses Maß an Vorsicht beim Einsatz dieser Elemente walten lassen müssen.

Die Bedeutung der einzelnen Elemente ist:

  • <title>: Mit diesem Element können Sie dem Browsertab einen sichtbaren Titel zuweisen. Suchmaschinen nutzen diese Information, um den Inhalt des Dokuments einzuordnen. Speichert ein Nutzer die aktuelle Ansicht als Bookmark, verwendet der Browser den Titel, um das Bookmark zu beschriften.

  • <meta>: Mit dem meta-Element können Sie viele verschiedene Informationen für Ihre Applikation beziehungsweise für die aktuelle Sicht festlegen. Die Möglichkeiten reichen von Informationen zur Darstellung wie dem Character-Set und dem Viewport über technische Aspekte wie die Cachesteuerung bis zu Daten für Suchmaschinen wie Beschreibung, Autor oder Schlüsselwörter.

  • <link>: Das link-Element verbindet externe Ressourcen mit dem aktuellen Dokument. Links können Sie verwenden, um Stylesheets zu laden, ein Favicon festzulegen, Schriften zu laden oder Ressourcen vorab zu laden.

AssetLoading

Bisher hat sich React beim asynchronen Laden primär auf Komponenten und Daten konzentriert und dafür die Features Suspense for Code Splitting und Suspense for Data Loading umgesetzt. Das neue Asset-Loading-Feature bringt die Idee hinter Suspense, also die Anzeige von Fallback-Inhalten, noch einen Schritt weiter, indem Sie auch statische Assets einschließen können. Zu solchen Assets gehören beispielsweise Stylesheets, Fonts, Skripte oder Bilder.

Das Grundproblem besteht darin, wie der Browser eine Webseite aufbaut. Zunächst wird das statische HTML gerendert; während dieses Prozesses lädt der Browser weitere Assets wie Bilder, Schriften oder zusätzliches CSS. Das kann zum FOUC, dem Flash of Unstyled Content, führen. Auch bei Bildern kann ein ähnlicher Effekt auftreten, wenn die Dateien noch nicht im Frontend vorhanden sind. In diesem Fall rendert der Browser das leere Element. Geben Sie hier nicht die korrekten Dimensionen an, verändert er die Größe des Elements, sobald das Bild heruntergeladen ist, und die Darstellung verschiebt sich.

Mit dem Asset-Loading-Feature kann React entscheiden, wann der Inhalt einer Komponente oder eines Komponentenbaums bereit für die Darstellung ist. Außerdem erhalten Sie eine Reihe neuer Funktionen im Rahmen der Resource Loading APIs für noch bessere Kontrolle, wann eine Ressource geladen und initialisiert werden soll.

  • preload: Mit dieser Funktion können Sie den Browser anweisen, eine Ressource zu laden. Dazu geben Sie den URL der Ressource und ein Optionsobjekt an. In diesem spezifizieren Sie mindestens mit der as-Eigenschaft den Typ wie font, image, script oder style. Sie können diese Funktion sowohl beim Rendern einer Komponente als auch in einem Event Handler nutzen. Beim Rendern können Sie so zu einem möglichst frühen Zeitpunkt mit dem Laden beginnen. In einem Event Handler können Sie diesen Prozess sogar schon beginnen, bevor Sie eine Komponente oder einen Komponentenbaum rendern.

  • preinit: Diese Funktion lädt Stylesheets und Skripte herunter. Anschließend werden die Skripte direkt im Browser ausgeführt. Die heruntergeladenen Stylesheets werden direkt in den Browser eingefügt und damit aktiviert. Auch hier können Sie die Funktion wieder in eine Komponente einfügen und beim Rendern ausführen oder in Event Handler einbinden.

Formulare in React 19

Das überarbeitete Formular-Handling in React 19 trägt der Tatsache Rechnung, dass Formulare ein wesentlicher Bestandteil der Interaktion in einer Applikation sind. Die Server Actions der vergangenen Versionen haben bereits einen ersten Schritt in diese Richtung getan. Diese Server Actions sind jedoch, wie der Name schon verrät, lediglich serverseitig verfügbar. Mit dem Konzept der Actions verbessert React das Formular-Handling auch clientseitig. Dazu gehören die folgenden Bestandteile:

  • Pending State: Wird ein Formular abgeschickt, kann eine Transition begonnen werden. Ist die Aktion des Formulars, also beispielsweise die Übermittlung der Daten zum Server, abgeschlossen, endet die Transition. Dieser Zustand kann auch dem Benutzer angezeigt werden.

  • Optimistic Updates: Der neue useOptimistic Hook kann dafür sorgen, dass die gewünschte Änderung sofort angezeigt wird, ohne dass die Aktion vollständig beendet ist. Hier besteht die Gefahr, dass die Aktion im Fehlerfall zurückgerollt werden muss.

  • Error Handling: Actions ermöglichen Fehlerbehandlung, mit deren Hilfe Sie die Fehler über den Standardmechanismus von React, also Error Boundaries, abfangen und anzeigen können.

  • Neue Props im form-Element: Der Kern der Actions sind die Props action und formActions des form-Elements.

Eine interessante Änderung betrifft einen bereits existierenden Hook: die useTransition-Funktion. Bisher konnten Sie diese Funktion verwenden, um den State einer Komponente zu aktualisieren, ohne das UI zu blockieren. Dafür haben Sie die startTransition-Funktion des Hooks verwendet. Während eine solche Transition aktiv ist, ist der Wert der isPending-Variablen des Hooks true. Bisher musste die Transition-Funktion synchron sein. Asynchrone State-Updates haben nicht funktioniert beziehungsweise wurden ignoriert. Das ändert sich mit React 19 und Sie können die Transitions-Funktion asynchron machen. Das ist also der erste Schritt zum Pending-State, aber es geht noch deutlich eleganter. Dafür müssen Sie jedoch etwas tiefer in die Trickkiste greifen.

Der Weg führt hierbei über den useActionState Hook. Diese Funktion erinnert sehr entfernt an den altbekannten State Hook von React, nur, dass er tief in die Formularbehandlung integriert ist. In Listing 3 sehen Sie, wie Sie diesen Hook mit einem Formular verwenden können.

Listing 3: Formular mit dem neuen useActionState Hook

import { useActionState } from 'react';

const Form: React.FC = () => {
  const [error, submitAction, isPending] = useActionState(
    async (prevState, formData) => {
      const person = Object.fromEntries(formData.entries());

      console.log(person);

      await new Promise((resolve) => setTimeout(() => resolve(null), 2000));

      // return 'Whoops'; // Fehlermeldung
    },
    null
  );

  return (
    <>
      {error && <div>Es ist ein Fehler aufgetreten: {error}</div>}
      <form action={submitAction}>
        <div>
          <label>
            Vorname: <input type="text" name="firstname" />
          </label>
        </div>
        <div>
          <label>
            Nachname: <input type="text" name="lastname" />
          </label>
        </div>
        <button disabled={isPending}>speichern</button>
      </form>
    </>
  );
};

export default Form;
 

Was hier als Erstes auffällt, ist, dass Sie nicht mit einem lokalen Formular-State oder Refs arbeiten müssen. Stattdessen greifen Sie auf die useActionState-Funktion zurück. Dieser übergeben Sie eine Funktion, die die Formularbehandlung für Sie übernimmt. Die Funktion erhält, ähnlich wie der State Hook, den vorherigen State und die aktuellen Formulardaten. Den State können Sie initial über das zweite Argument der useActionState-Funktion definieren. Ansonsten ist es der Rückgabewert der ActionState-Funktion. In diesem Beispiel nutzen Sie den State, um Fehler bei der Datenverarbeitung festzuhalten. Die Formulardaten liegen als FormData-Objekt vor. Mit der formEntries-Methode können Sie es in ein reguläres JavaScript-Objekt umwandeln. Die ActionState-Funktion kann asynchron sein. Sie können also wie im Beispiel auf ein Time-out warten, um den Pending-State zu simulieren oder Sie sprechen über das Fetch API mit einer Backend-Schnittstelle. Der Rückgabewert in der Funktion sorgt dafür, dass die Anzeige der Fehlermeldung ausgelöst wird.

Die useActionState-Funktion gibt Ihnen ein Array mit drei Elementen zurück. Das erste ist der State, in unserem Fall null oder eine Fehlermeldung, und das zweite ist die submitAction-Funktion, die Sie mit der action-Prop des form-Elements verbinden. Der useActionState Hook sorgt übrigens dafür, dass die Standardaktion des Formulars unterbunden wird, wirkt also wie ein preventDefault beim Submit, und das Formular wird nach dem Absenden automatisch geleert. Das dritte Element im Rückgabearray ist der Pending-State, den Sie, wie im Beispiel, verwenden können, um den Submit-Button während der Verarbeitung zu deaktivieren.

Eine weitere interessante Funktion ist in diesem Zusammenhang der useFormStatus Hook. Er gibt Ihnen Informationen über den aktuellen Formularzustand in Form eines Objekts mit den folgenden Eigenschaften:

  • pending: Wurde das Formular abgeschickt und wird gerade verarbeitet, weist diese Eigenschaft den Wert true auf.

  • data: Mit der data-Eigenschaft erhalten Sie Zugriff auf das FormData-Objekt des Formulars mit allen Werten, die Sie eingegeben haben.

  • method: Diese Eigenschaft gibt an, mit welcher HTTP-Methode das Formular abgeschickt wurde. Den Wert dieser Eigenschaft können Sie mit der method-Prop des form-Elements beeinflussen.

  • action: Mit der action-Eigenschaft können Sie auf eine Referenz der Funktion zugreifen, die Sie der action-Prop des form-Elements übergeben haben.

Damit der useFormStatus Hook funktioniert, müssen Sie ihn in einer Komponente verwenden, die innerhalb eines form-Elements gerendert wird. Von genau diesem Formular bekommen Sie dann die Informationen.

Nützlich ist dieser Hook beispielsweise, wenn Sie die Elemente eines Formulars in Komponenten unterteilen und dort auf bestimmte Informationen zugreifen möchten. In Listing 4 sehen Sie als Beispiel die Implementierung einer SubmitButton-Komponente.

Listing 4: SubmitButton-Komponente mit useFormStatus Hook

import { useFormStatus } from 'react-dom';

const SubmitButton: React.FC = () => {
  const status = useFormStatus();

  return (
    <button type="submit" disabled={status.pending}>
      speichern
    </button>
  );
};

export default SubmitButton;
 

Betätigen Sie den Button, wird das Formular abgeschickt, die pending-Eigenschaft aus dem useFormStatus Hook nimmt den Wert true an und sorgt dafür, dass der Button deaktiviert wird.

useOptimistic

Optimistische Updates sind ein Pattern, das Sie nicht nur in React-Applikationen, sondern weit verbreitet in der Webentwicklung finden. Dabei gehen Sie davon aus, dass die Operation zumindest mit einer hohen Wahrscheinlichkeit erfolgreich sein wird. Ein Beispiel hierfür ist das Ändern des Anzeigenamens in einer Applikation. Verändern Sie die Information, soll sie einerseits direkt in der Oberfläche angezeigt, aber auf der anderen Seite auch auf dem Server gespeichert werden. In Listing 5 sehen Sie, wie der Code für das Beispiel aussieht.

Listing 5: Formular mit optimistischem Update

import { useOptimistic, useActionState } from 'react';

function updateName(name: string): Promise<string> {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(name);
    }, 2000);
  });
}

const Optimistic: React.FC = () => {
  const [name, setName] = react.useState('');
  const [optimisticName, setOptimisticName] = useOptimistic(name);

  const [, submitAction, isPending] = useActionState(
    async (prevState, formData) => {
      const name = formData.get('name');
      setOptimisticName(name);
      const updatedName = await updateName(name);
      setName(updatedName);
    }
  );

  return (
    <>
      <div>{optimisticName}</div>
      <form action={submitAction}>
        <label>
          Name: <input type="text" name="name" />
        </label>
        <br />
        <button disabled={isPending}>ändern</button>
      </form>
    </>
  );
};

export default Optimistic;
 

Die useOptimistic-Funktion rufen Sie mit einem Wert auf, das ist die Source of Truth. Dieser Wert kommt üblicherweise vom Server. In unserem Beispiel stammt der Wert aus einem lokalen State. Als zweites Argument können Sie eine Funktion übergeben, die den bisherigen Wert und den optimistisch zu ändernden Wert enthält. Das ist vor allem interessant, wenn Sie Listen optimistisch aktualisieren möchten. Als Rückgabe erhalten Sie den aktuellen Wert und eine Funktion, mit der Sie den Wert setzen können, was dem Rückgabewert des State Hooks entspricht.

Schicken Sie nun das Formular ab, setzen Sie zunächst den optimistischen Wert und React rendert die Komponente sofort mit dem neuen Wert. Anschließend wird der Name persistiert und der State entsprechend aktualisiert. Läuft alles fehlerfrei ab, bleibt der Anzeigename unverändert. Tritt jedoch ein Fehler auf und würde der State beispielsweise zurückgesetzt, übernimmt useOptimistic diesen Wert und die Anzeige wird entsprechend geändert.

use – der Universal-Hook, der kein Hook ist

Das use-Präfix kennen wir seit der Einführung des Hook API. Diese drei Buchstaben lassen uns bei der Entwicklung einer Applikation aufhorchen, es gelten ja schließlich die Rules of Hooks, an die wir uns halten müssen. Mit React 19 kommt eine neue Funktion hinzu: die use-Funktion. Und ja, ich schreibe hier bewusst „Funktion“ und nicht „Hook“, denn für use gelten andere Regeln. Sie dürfen diese Funktion beispielsweise bedingt aufrufen, was bei regulären Hooks nicht erlaubt ist.

Diese Funktion hat zwei Aufgaben. Zum einen hilft sie Ihnen, mit Promises zu arbeiten. Übergeben Sie der use-Funktion ein Promise-Objekt, nutzt React so lange den zugehörigen Suspense-Fallback, bis das Promise-Objekt resolvt ist. Die Schattenseite dieser Funktion ist, dass das nicht für Promises gilt, die direkt in einer Clientkomponente oder in einem Hook erzeugt wurden. Damit das funktioniert, müssen Sie auf eine Bibliothek zugreifen, die kompatibel mit Suspense ist und Promises cachen kann.

Die zweite Einsatzmöglichkeit der use-Funktion ist der Ersatz der useContext-Funktion. Sie können der use-Funktion ein Kontextobjekt übergeben und erhalten den Wert des Kontexts zurück. Sie sparen sich damit also ein paar Zeichen.

Das sind die beiden ersten Anwendungsfälle der use-Funktion. Das React-Team plant aber noch zusätzliche Erweiterungen, wir dürfen also gespannt sein, was noch auf uns zukommt.

Weitere Änderungen

Das React-Team hat für React 19 nicht nur an neuen Features gearbeitet, sondern auch noch zahlreiche Verbesserungen der bestehenden Funktionalität eingebaut. Ein Beispiel ist, dass Funktionskomponenten eine ref Prop unterstützten, die denselben Zweck wie eine forwardRef erfüllt, also Referenzen an Komponenten weiterzureichen. Die ref Prop wird in Zukunft die forwardRef-Funktion ersetzen, sodass diese in nächster Zeit als deprecated markiert werden wird.

Eine weitere Verbesserung betrifft das Context-Feature von React. Ursprünglich haben Sie mit Context-Providern und Context-Consumern gearbeitet. Der Consumer ist dank des useContext Hooks beziehungsweise seit React 19 der use-Funktion nicht mehr wirklich relevant. Ein ähnliches Schicksal ereilt jetzt auch den Provider. Statt wie bisher <MyContext.Provider value=“xxx“> zu schreiben, reicht ab React 19 ein <MyContext value=“xxx“> aus.

Fazit

React 19 ist vollgepackt mit neuen Features, über die sich sowohl Autoren von Bibliotheken, aber auch Entwickler:innen von React-Applikationen freuen. Einige der Schnittstellen haben das Potenzial, die Art und Weise, wie wir unsere Applikationen entwickeln, grundlegend zu beeinflussen, wie der Compiler oder was den Umgang mit Formularen betrifft. Andere Features sind logische Schritte in der Evolution der Bibliothek, wie beispielsweise die Änderung im Context API.

Insgesamt finde ich es sehr erfreulich, dass das React-Team auf die alltäglichen Probleme und Wünsche der Community eingeht und die Bibliothek kontinuierlich weiterentwickelt und verbessert. Auch, wenn ich mir einen kürzeren und planbareren Releasezyklus, wie ihn andere Projekte haben, wünschen würde.

Stay tuned

Bei neuen Artikel & Eventupdates informiert werden:

Top Articles About React

Bleiben Sie auf dem Laufenden!

mit wöchentlichen Event-& Branchen-Updates

Bleiben Sie auf dem Laufenden!

mit wöchentlichen Event-& Branchen-Updates

Nichts mehr verpassen!