Startseite / Artikel / React-Fragmente und StrictMode: Effizienteres Markup und frühere Fehlererkennung

React-Fragmente und StrictMode: Effizienteres Markup und frühere Fehlererkennung

Erfahren Sie, wie React Fragmente Elemente ohne zusätzliche DOM-Wrapper gruppieren und wie die ausschließlich für die Entwicklung vorgesehene doppelte Aufrufung von StrictMode unreine Renderungen sowie Effekte aufdeckt.

976 Wörter

Zwei React-Funktionen zeichnen zwar nie etwas auf dem Bildschirm, prägen aber fast jeden Komponentenbaum: Fragmente und StrictMode. Fragmente halten Ihren DOM frei von Wrapper-Elementen, die nur dazu da sind, JSX zu erfüllen, während StrictMode Ihre Komponenten in der Entwicklung absichtlich unter Druck setzt, damit unreine Logik bereits vor den Nutzern sichtbar wird.

Warum JSX eine einzige Wurzel benötigt

JSX kompiliert jeden Tag in einen Funktionsaufruf, wodurch zwei zusammen zurückgegebene Geschwister-Tags zwei Werte sind, obwohl eigentlich nur ein Wert erwartet wird. Dadurch fehlt die Kompilierung:

return (
  <h1>Hello</h1>
  <p>Welcome</p>
);

Die traditionelle Workaround-Lösung bestand darin, die Geschwister in einen div einzubetten:

return (
  <div>
    <h1>Hello</h1>
    <p>Welcome</p>
  </div>
);

Der zusätzliche Knoten hat Nachteile: Er vertieft den DOM, stört Flex- oder Grid-Anordnungen, die direkte Kinder erwarten, erschwert Selektoren und kann zu ungültigem oder weniger zugänglichem HTML führen.

Gruppierung ohne Wrapper

Ein Fragment gruppiert Kinderkomponenten in React, ohne ein DOM-Element zu erzeugen. Die kurze Syntax besteht aus einem leeren Tagpaar:

return (
  <>
    <h1>Hello</h1>
    <p>Welcome</p>
  </>
);

Die explizite Form React.Fragment erfüllt denselben Zweck und ist erforderlich, wenn man ein Prop übergeben muss:

return (
  <React.Fragment>
    <h1>Hello</h1>
    <p>Welcome</p>
  </React.Fragment>
);

Die gerenderte HTML-Struktur enthält nur h1 und p. Der Leistungsvorteil ist gering; der eigentliche Nutzen liegt in der korrekten Markup-Struktur.

Rückgabe von Geschwisterelementen

Eine Komponente, die mehrere gleichwertige Elemente erzeugt, kann das Layout ihrem Elternelement überlassen:

function Card() {
  return (
    <>
      <h2>Title</h2>
      <p>Description</p>
    </>
  );
}

Key-basierte Fragmente in Listen

Beim Durchlaufen von Daten, bei denen jedes Element mehr als ein Element erzeugt, benötigt React weiterhin einen stabilen key pro Element. Die kurze <>-Syntax kann keine Attribute entgegennehmen, daher sollte React.Fragment mit einem Key verwendet werden:

items.map(item => (
  <React.Fragment key={item.id}>
    <h2>{item.title}</h2>
    <p>{item.description}</p>
  </React.Fragment>
));

Tabellelemente und -zeilen

HTML-Tabellen haben ein strenges Inhaltsmodell: Ein tr darf nur td- oder th-Elemente enthalten. Ein umschließendes div ist dort ungültig. Ein Fragment ermöglicht es einem Komponenten, mehrere Zellen zu einer von seinem Elternelement verwalteten Zeile beizutragen:

function Row() {
  return (
    <>
      <td>A</td>
      <td>B</td>
    </>
  );
}

Was prüft StrictMode

StrictMode rendernt nichts, fügt keine Elemente hinzu und hat in Produktversionen keinerlei Auswirkung. In der Entwicklung aktiviert es zusätzliche Prüfungen, darunter:

  • Warnungen für als unsicher erachtete Methoden des Klassenzyklus aus der Vergangenheit, wie zum Beispiel componentWillMount
  • Warnungen für veraltete APIs wie String-Referenzen und findDOMNode
  • Zweimalige Aufrufe von Komponentenkörpern, Initialisierungs- und Aktualisierungsfunktionen, um unreine Renderungen aufzudecken
  • Ausführung von Effekten durch einen zusätzlichen Setup-, Cleanup- und Setup-Zyklus beim Einbinden, um fehlenden Cleanup zu erkennen

Die genaue Liste hat sich in verschiedenen React-Versionen geändert, daher sollten Sie die aktuelle Dokumentation für Ihre Version prüfen.

Beabsichtigte doppelte Darstellung

Betrachten Sie ein Komponente, das während der Darstellung Protokolle anlegt:

function App() {
  console.log("Rendered!");
  return <h1>Hello</h1>;
}

Unter StrictMode während der Entwicklung wird die Meldung zweimal im Konsole angezeigt:

Rendered!
Rendered!

Das ist beabsichtigt. Eine Render-Funktion sollte rein sein: Bei denselben Props und State gibt sie immer dasselbe Ergebnis zurück und ändert nichts außerhalb ihrer selbst. Wenn sie zweimal aufgerufen wird, führen Verstöße wie die Änderung einer gemeinsam genutzten Variablen zu offensichtlich falschen Ergebnissen. Reine Funktionalität ist wichtig, da parallele Renderungen starten, pausieren, ignoriert oder wiederholt werden können – Code, der davon ausgeht, dass pro Aktualisierung genau ein Render stattfindet, bricht unter diesen Bedingungen zusammen.

Aktivieren

Umgeben Sie den Baum, der überprüft werden soll – in der Regel die gesamte App – am Wurzelebene:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Effekte unter StrictMode: Der Abhängigkeitsarray ist nicht die Lösung

Hier ist ein Effekt ohne Abhängigkeitsarray. Er wird nach jeder Renderung ausgeführt, wodurch jede Aktualisierung eine weitere Anfrage auslöst:

useEffect(() => {
  console.log("Fetching...");
  fetch("/api");
});

Das Hinzufügen eines leeren Arrays beschränkt den Effekt auf das Einbinden:

useEffect(() => {
  console.log("Fetching...");
  fetch("/api");
}, []); // stable dependency

Diese Änderung ist korrekt, stoppt aber die doppelte Anfrage in der Entwicklung nicht. Seit React 18 montiert StrictMode das Komponente, führt seine Aufräumfunktion aus und montiert es erneut, sodass ein Effekt mit [] dort immer noch zweimal ausgeführt wird. Der doppelte Ausführungsvorgang ist das Signal, nicht der Fehler. Die eigentliche Lösung ist eine Aufräumfunktion, die eine zweite Ausführung harmlos macht – typischerweise indem sie die erste Anfrage mit einem AbortController abbricht oder ihr Ergebnis mit einem Flag ignoriert. Ein solcher Effekt ist außerdem gegen echte Neumontagen in der Produktion sicher.

Vergleich

  • Zweck: Fragments gruppieren Elemente; StrictMode macht Fehler sichtbar.
  • DOM-Ausgabe: Keines der beiden fügt ein Element hinzu.
  • Einfluss in der Produktion: Keiner für beide.
  • Betriebsverhalten: Fragments werden normal dargestellt; StrictMode ruft Darstellungen und Effekte nur in der Entwicklung doppelt auf.
  • Vorteil: Sauberere, gültige Markup-Strukturen im Vergleich zu vorhersehbarem Code ohne Nebeneffekte.

Eine minimale Einrichtung zum Ausprobieren

Die untenstehende Komponente gibt zwei Geschwisterelemente über ein Fragment zurück:

export default function App() {
  return (
    <>
      <h1>Hello World</h1>
      <p>Rendered using Fragments</p>
    </>
  );
}

Die Eingabedatei rendernt sie innerhalb von StrictMode, sodass jede unreine Logik, die später hinzugefügt wird, sofort in der Entwicklung erkannt wird:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Wichtige Erkenntnisse

  • Nutzen Sie ein Fragment, sobald ein Wrapper-Element nur zum Erfüllen der JSX-Anforderungen notwendig wäre, und verwenden Sie React.Fragment mit einem key in Listen.
  • Lassen Sie StrictMode in der Entwicklung aktiv; doppelte Protokollierungen und Effekte dienen absichtlich als Diagnosewerkzeuge.
  • Beseitigen Sie doppelt ausgeführte Effekte durch ordnungsgemäße Bereinigung, anstatt an Abhängigkeiten herumzufummeln.
  • Zusätzliche Literatur