Aus Linux-Magazin 04/2025

React 19 erweitert seine Kompetenzen in Richtung Server

© lesstalk / 123RF.com

Mit Version 19 baut die quelloffene Javascript-Bibliothek React ihre Fähigkeiten stark aus. Die Software bringt viele Detailverbesserungen und arbeitet nicht mehr nur im Browser des Benutzers, sondern auch auf den Servern des Anbieters.

Zwar ist das Server Side Rendering ein alter Hut in gängigen Javascript-Frameworks, aber mit Server Components unterscheidet React zwei Anwendungsfälle: Ausführung zur Build Time oder zur Load Time. Daten, die der Server zur Laufzeit vom Server benötigt, lädt die Anwendung über Server Actions in den Browser, ohne dabei noch auf ein Backend in PHP, Node, Python oder Java angewiesen zu sein. React erlangt in der aktuellen Version mithilfe seiner Frameworks selbst Full-Stack-Fähigkeit.

Der vorliegende Artikel demonstriert beide Server-Features anhand der Beispielanwendung Token Checker (Abbildung 1). Der Anwender gibt das Token in den rechteckigen Eingabebereich ein oder kopiert es dorthin. Sobald er auf den Knopf unterhalb des Formularfelds drückt, berechnet die Software den Wert der Shannon-Entropie über eine Server Action auf dem Webserver des Anbieters und gibt das Ergebnis unter dem Formular aus. Der Link Info in Abbildung 1 öffnet oder schließt einen Erklärungstext zur Shannon-Entropie. Der Text wird bereits zur Build Time der Anwendung mit einer Server Component in die App geladen.

Abbildung 1: Der Token Checker berechnet die Entropie auf dem Server.

Abbildung 1: Der Token Checker berechnet die Entropie auf dem Server.

Die Entropie dient als physikalisches Maß für Unordnung. Ihre Berechnung nach Ludwig Boltzmann [1] verhalf dem atomistischen Weltbild ab Beginn des 20. Jahrhunderts zum Durchbruch. Ein gewisser Claude E. Shannon übertrug um das Jahr 1948 den Begriff in die Informationstheorie. Abbildung 2 veranschaulicht die Wirkung der Entropie anhand von vier Token: Je höher die Entropie S, desto zufälliger wirkt der Zeichensalat.

Ein bekannter Anwendungsfall in der Informationstheorie findet sich in der Berechnung der Stärke eines Passworts. In der Praxis ist es einfacher, für die eingegebene Zeichenkette das Überschreiten eines Schwellwerts einzufordern, als das Vorkommen von Sonderzeichen in Passwortkandidaten abzufragen. Dasselbe gilt für Tokens, die zwecks Authentifizierung im Kopf eines HTTP-Requests zum Einsatz kommen: Je chaotischer, desto sicherer, denn der Zufall kann per Definition nicht deterministisch sein.

Abbildung 2: Mit Zunahme der Shannon-Entropie&nbsp;<span class="ui-element">S</span> verschwindet die Ordnung.

Abbildung 2: Mit Zunahme der Shannon-Entropie S verschwindet die Ordnung.

Installation

Abbildung 3 belegt anhand der Downloadzahlen [2] von Nodes Package Repository Npm, dass React immer noch die erste Wahl beim Erstellen von Webanwendungen im Browser mit Javascript ist. Anders als hinter Angular [3] und Vue.js [4] steckt hinter React jedoch kein Framework. Zudem benötigt es beim Umsetzen der Server Components und Server Actions die technische Hilfe von Frameworks wie Next.js [5], Remix [6], Gatsby [7] oder Expo [8] (React Native).

Abbildung 3: Im Jahr&nbsp;2013 erstmals erschienen, ist React mit 25&nbsp;Millionen Downloads t&auml;glich immer noch f&uuml;hrend.

Abbildung 3: Im Jahr 2013 erstmals erschienen, ist React mit 25 Millionen Downloads täglich immer noch führend.

Dieser Artikel demonstriert Reacts Server-Funktionalität mit Next.js, das der Code aus Listing 1 in der Shell installiert. Der Aufruf aus der ersten Zeile integriert Nodes Version Manager Nvm, der Befehl in der zweiten Zeile installiert die aktuelle Version Node 22. Das Kommando aus Zeile 3 startet das Erstellen eines Next.js-Projekts mit einer Reihe von Prompts in der Shell.

Listing 1

Node unter Ubuntu installieren

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
$ nvm install 22
$ npx create-next-app@latest
$ npm i binary-shannon-entropy html-react-parser markdown-it
$ npm i --save-dev @types/markdown-it

Sämtliche vorgeschlagenen Defaults können Sie wie in Abbildung 4 bestätigen. Das zunächst leere Next.js-Projekt arbeitet mit der Version 15 und einem Release Candidate von React 19. Die folgenden Listings veranschaulichen alle im Projektordner für den Token Checker geänderten oder neu erstellten Dateien mit relativer Pfadangabe. Die CSS-Formatanweisungen in der Datei »./src/app/globals.css« führen wir nicht auf, sie finden sich aber zusammen mit allen anderen Listings im Downloadbereich zu diesem Artikel.

Der Befehl »npx next dev« startet im Projektverzeichnis den Watch-Prozess des Build-Tools von Next.js in der Shell. Den Entwicklungsstand können Sie im Browser unter »http://localhost:3000« nachverfolgen und testen. Die vorletzte Zeile von Listing 1 importiert die zum Umsetzen des Token Checkers nötigen Npm-Module, die letzte zieht Typdefinitionen für das verwendete Modul »markdown-it« nach.

Abbildung 4: Next.js-Prompts nach der Konfiguration des Token Checkers. Die vorgeschlagenen Standards k&ouml;nnen Sie allesamt abnicken.

Abbildung 4: Next.js-Prompts nach der Konfiguration des Token Checkers. Die vorgeschlagenen Standards können Sie allesamt abnicken.

Full Stack Vision

Version 19 erweitert React zu einer Full-Stack-fähigen Bibliothek. Abbildung 5 gibt einen Überblick über die Arbeitsweise.

Abbildung 5: Die Aufgabenverteilung in einer React-basierten Anwendung.

Abbildung 5: Die Aufgabenverteilung in einer React-basierten Anwendung.

Die Server Components rechts unten werden zur Build Time auf dem Rechner des Entwicklers beziehungsweise in der CI/CD-Pipeline des Projekts oder zur Load Time vom Webserver der React-Anwendung ausgeführt. Client Components laufen nach dem Laden der React-Anwendung exklusiv im Webbrowser.

Die Client Components ihrerseits können zur Laufzeit wiederum Server Actions im Stil eines Backends ansprechen. Server Components sind nicht interaktiv, dürfen aber die interaktiven Client Components verwenden. Umgekehrt funktioniert das nicht. Abbildung 5 zeigt die erlaubten Zugriffe (rechts im Bild).

Components

Den Einstiegspunkt der Beispiel-App zeigt die Datei »./src/app/page.tsx« aus Listing 2. Die Kommandos aus den Zeilen 1 bis 3 importieren zunächst die drei Custom-Components Checker, Reader und Footer. Eine Custom Component beschreibt ein wiederverwendbares Fragment der React-Anwendung. Es lässt sich im JSX-Code (Zeile 6 bis 11) wie jedes andere HTML-Tag nutzen. Da eine Javascript-Engine JSX-Code nicht ausführen kann, übersetzt React ihn zuvor in lauffähigen Javascript-Code.

Die Client Component »Checker« aus Zeile 7 wird in das interaktive Eingabeformular aus Abbildung 1 umgewandelt. Aus der Server Component »Reader« (Zeile 9) entsteht bereits zur Build Time ein HTML-Fragment. Die Client Component »Footer« (Zeile 8 bis 10) wird zur interaktiven Fußzeile unterhalb des Formulars aus Abbildung 1.

Listing 2

Einstiegspunkt (./src/app/page.tsx)

import Checker from './checker';
import Reader from "./reader";
import Footer from "./footer"
export default function Home() {
  return (
    <div id="page">
      <Checker/>
      <Footer>
        <Reader path="./public/Shannon.md"/>
      </Footer>
    </div>
  );
}

Server Components

Listing 3 greift noch einmal die Server Component »Reader« aus Zeile 9 von Listing 2 auf. Die Typdeklaration (Zeilen 4 bis 6) verlangt Typescript, um den Aufrufparameter »path« aus Zeile 9 von Listing 2 zu beschreiben.

Die Component selbst (Listing 3, Zeile 7 bis 11) liest in Zeile 8 eine Markdown-Datei über ihren Pfad aus dem Verzeichnissystem der Build-Umgebung und konvertiert sie in Zeile 9 in ein HTML-Fragment.

Bei genauem Hinsehen erkennen Fachleute, dass Listing 3 ohnehin nur auf einem Server unter Node laufen kann, da das in Zeile 1 importierte Modul fs von dessen Laufzeitumgebung abhängt. Das Ergebnis des Aufrufs in Zeile 10 von Listing 3 ist der Erklärungstext über die Shannon-Entropie (Listing 4).

Listing 3

Markdown nach HTML (./src/app/reader.tsx)

import { promises as fs } from 'fs';
import parse from 'html-react-parser';
import markdownIt from "markdown-it";
type ReaderParams = {
  path: string;
};
export default async function Reader({path}: ReaderParams) {
  const content = await fs.readFile(path, 'utf8');
  const converter = new markdownIt();
  return parse(converter.render(content));
}

Listing 4

Erklärungstext (./public/Shannon.md)

## Shannon Entropie
* einfach gesagt: die durchschnittliche Anzahl von Entscheidungen (Bits), die benötigt werden, um ein Zeichen aus einer Zeichenmenge zu identifizieren oder zu isolieren,
* anders gesagt: ein Maß, welches für eine Nachrichtenquelle den mittleren Informationsgehalt ausgegebener Nachrichten angibt.
Das informationstheoretische Verständnis des Begriffes Entropie geht auf Claude E. Shannon zurück und existiert seit etwa 1948.

Um Server Components erst zur Load Time auszuführen, sind Sie auf die Gunst von Next.js angewiesen. React bietet keine Möglichkeit, das explizit vorzuschreiben. Bauen Sie mit »npx next build« die vollständige App, findet der Befehl »grep« im Build-Verzeichnis unter ».next« den Erklärungstext.

Alternativ lassen Sie in Listing 3 nicht das Dateisystem die Markdown-Datei lesen, sondern verwenden stattdessen folgenden Ausdruck:

await fetch(path, { cache: 'no-store' })

Hier unterbindet der zweite Parameter nicht nur das Puffern des Aufrufs, sondern veranlasst auch das Rendern zur Load Time.

Verändern Sie nach dem Build den Erklärungstext unter »./public/Shannon.md«, taucht die Änderung nach dem Laden der Anwendung im Browser auf. Die Kriterien für das Rendern zur Load Time bespricht die Dokumentation [9] von Next.js.

Client Components

Die Direktive »”use client”« in der ersten Zeile von Listing 5 macht die Component »Footer« (Zeilen 6 bis 25) explizit zu einer Client Component.

Der Ausdruck »React.FC<Props>« aus Zeile 6 übernimmt die Typdeklaration »Props« aus den Zeilen 3 bis 5 als Typ des Aufrufobjekts der Funktion »{children}«. Der Ausdruck »children: React.ReactNode« aus Zeile 4 lässt den Code aus Listing 5 das vom Server Component Reader in Listing 2 erzeugte HTML-Fragment in der Variablen »children« erwarten.

In der Zeile 7 von Listing 5 verwendet die Component den React Hook »useState«, um den Erklärungstext über die Shannon-Entropie über den Link in den Zeilen 13 bis 16 ein- und ausblenden zu können. Initial wird in der Statusvariable »isOpen« der Wert »false« gespeichert.

Die Funktion »setIsOpen« dient dazu, den Wert der Statusvariablen zu ändern. Ein Mausklick auf den Link-Text »Info« ruft über den Callback-Handler »onClick« in Zeile 13 die Funktion »toggleDiv« auf (Zeilen 8 bis 10). Sie invertiert in Zeile 9 jeweils den in »isOpen« vorliegenden booleschen Wert durch Verneinung und Speichern mit »setIsOpen«.

Ist der Wert von »isOpen« gerade »true«, sorgt der Code aus den Zeilen 18 bis 22 dafür, dass das HTML-Fragment mit dem Erklärungstext aus der Variablen »children« erscheint. Im Übrigen verhindert der Ausdruck »e.preventDefault()« in Zeile 14, dass das Klick-Event durch den DOM-Baum propagiert und unerwünschte Seiteneffekte hervorruft.

Listing 5

Footer (./src/app/footer.tsx)

"use client";
import React, { useState } from 'react';
type Props = {
  children: React.ReactNode;
};
const Footer: React.FC<Props> = ({children}) => {
  const [isOpen, setIsOpen] = useState(false);
  const toggleDiv = () => {
    setIsOpen(!isOpen);
  };
  return (
    <div id="footer">
      <a href="#" onClick={(e) => {
        e.preventDefault();
        toggleDiv();
      }}>Info</a>
      <hr/>
      {isOpen && (
        <div>
          {children}
        </div>
      )}
    </div>
  );
}
export default Footer;

Server Action

Server Actions lassen sich im Gegensatz zu Server Components sehr wohl aus Client Components heraus aufrufen, wie die Client Component »Checker« aus Listing 6 beweist.

Der Code aus den Zeilen 6 bis 17 erstellt das Eingabeformular aus Abbildung 1. Dem Title-Tag in Zeile 10 folgt das Eingabeformular (Zeilen 9 bis 13) mit dem Eingabebereich »textarea« in Zeile 10. Der Knopf in Zeile 12 versendet das Formular an den Webserver.

Listing 6

Eingabeformular (./src/app/checker.tsx)

"use client";
import { useActionState } from 'react';
import {Entropy, EntropyState} from './entropy';
export default function Checker() {
  const [state, action] = useActionState<EntropyState, FormData>(Entropy, {previous: "", entropy: 0});
  return (
    <>
      <h1>Token Checker</h1>
      <form action={action}>
        <textarea name="text" defaultValue={state.previous}></textarea>
        <br/>
        <button type="submit">Berechnen</button>
      </form>
      <h3>Ergebnis: {state.entropy.toFixed(3)}</h3>
    </>
  );
}

Anstelle einer URL steht im Action-Attribut des Tags »form« in Zeile 9 die Referenz »action«. Sie ist über den Hook »useActionState« in Zeile 5 praktischerweise direkt mit der Server-Action »Entropy« aus Listing 7 kurzgeschlossen.

Grundsätzlich könnten Sie auf den Umweg über den Hook verzichten, doch er erlaubt das Behandeln des Rückgabewerts der Server Action (Listing 7, Zeilen 7 bis 10) über die Status-Variable »state«. Die Struktur entspricht der Typdeklaration in den Zeilen 2 bis 5 von Listing 7.

Die Komponente »entropy« speichert die berechnete Shannon-Entropie. Sie wird in Zeile 13 auf die dritte Nachkommastelle gerundet unterhalb des Formulars ausgegeben. Da React den Eingabebereich beim Klick auf den Berechnen-Button löscht, holt sich die Anwendung den Eingabetext aus der Komponente »previous« der Statusvariablen »state« zurück und zeigt den Eingabetext stattdessen über das Attribut »defaultValue« an (Listing 6, Zeile 10).

An dieser Stelle ist ohnehin Vorsicht geboten, denn die scheinbar lokale Verknüpfung der Server Action in der Client Component geht in Wirklichkeit im Stil eines Remote Procedure Calls über Systemgrenzen hinweg.

Die implizite Auflösung des Server-Aufrufs erspart dem Entwickler die Handhabung von XHTTP-Requests gegenüber einem fremden Backend respektive den Einsatz eines GraphQL-Links. Der Ausdruck in spitzen Klammern »<EntropyState, FormData>« (Listing 6, Zeile 5) erklärt dem Hook »useActionState« die Signatur der Aufrufliste von »Entropy« (Listing 7, Zeile 6).

Listing 7

Server Action (./src/app/entropy.tsx)

"use server";
export type EntropyState = {
  previous: string,
  entropy: number
}
export async function Entropy(state: EntropyState, payload: FormData): Promise<EntropyState> {
  return {
    previous: payload.get('text') as string,
    entropy: shannon(payload.get('text') as string ?? '')
  }
   function shannon(token: string): number {
    const length = token.length;
    const frequencies: Record<string,number> = {};
    for (const char of token) {
      frequencies[char] = (frequencies[char] || 0) + 1;
    }
    let res = 0;
    for (const char in frequencies) {
      const frequency = frequencies[char];
      const probability = frequency / length;
      res -= probability * Math.log2(probability);
    }
    return res;
  }
}

Entropie

Die Server Action »Entropy« in Listing 7 erinnert wie Listing 3 eher an ein Node-Programm. Die Direktive »”use server”« in der ersten Zeile weist das Code-Fragment explizit als Server Action aus. Die Typdeklaration (Zeilen 2 bis 5) beschreibt den Rückgabewert der Server Action (Zeilen 6 bis 10). Die Funktion übernimmt in Zeile 6 im ersten Parameter den aktuellen Status »state«, im zweiten Parameter das Formular-Objekt »FormData«. Beides stammen aus dem vorherigen Listing 6 (dort aus Zeile 5).

Den Wert der Shannon-Entropie berechnet die Funktion »shannon« (Listing 7, Zeilen 11 bis 24). Die Implementierung erstellt in den Zeilen 14 bis 16 die Verteilung der Zeichen des in Abbildung 1 eingegebenen Texts im Histogrammstil und berechnet daraus in den Zeilen 18 bis 22 die Entropie als Mittelwert über den Informationsgehalt jedes Zeichens.

Als Informationsgehalt eines Zeichens schlug Shannon vor, die Wahrscheinlichkeit, mit der ein Zeichen in der Zeichenkette auftritt (Zeile 20) reziprok zu nehmen und pro Bit gemittelt über den Logarithmus zur Basis 2 auszudrücken, also als »Math.log2(probability)«. Im Grunde beurteilt Shannon nicht das vorliegende Token, sondern die Ausdrucksstärke der zugrunde liegenden Verteilung. Je mehr verschiedene Token sich damit bilden lassen, desto mehr Informationen kann man damit kodieren.

Fazit

In Version 19 bietet React mit den Server Components eine intelligente Möglichkeit, Content bereits serverseitig zur Build oder Load Time zu rendern. Damit entfallen unnötige Server Round Trips, was die Latenz der React-App entscheidend verbessern kann.

Server Actions lassen React-basierte Frameworks zu einer Full-Stack-Lösung avancieren. Doch der Einsatz bleibt abzuwägen: Während es mit GraphQL gelang, eine flexible Kapselung der Datenquelle herzustellen, verlangt der Full-Stack-Ansatz dem Entwickler detailliertes Wissen über die Datenspeicherung des Anbieters ab. Damit bietet er nicht unbedingt eine Zeitersparnis, aber eine Option für generische Datenspeicher. (csi/jlu)

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 5 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben