React & Co.

Aus Jonas Notizen Webseite
Zur Navigation springen Zur Suche springen
Offizielles ReactJS-Icon

React ist eine JavaScript-Bibliothek von Facebook, die sich zu einer sehr beliebten Technologie für die Erstellung dynamischer, testbarer und performanter Benutzeroberflächen entwickelt hat. Obwohl es kein vollwertiges Framework (Grundgerüst) wie Angular oder Ember ist, ist React aufgrund seiner Konzentration auf funktionale komponenten-basierte Ansichten ohne (oder minimalen) internen Zustand eine gute Wahl für die Erstellung von Einzelseitenanwendungen (SPA) oder sogar dynamischen Unterkomponenten innerhalb einer seiten-basierten Anwendung.

Inhaltsverzeichnis

React Einführung - Kernkonzepte

Das Modell von React verspricht durch die Konzepte des unidirektionalen Datenflusses und des „Virtual DOM“ den einfachen, aber trotzdem hochperformanten Aufbau auch komplexer Anwendungen.

Keine vollständige Anwendungsbibliothek

Da sich React nur mit dem Rendern von Daten an das DOM befasst, erfordert die Erstellung von React-Anwendungen in der Regel die Verwendung zusätzlicher Bibliotheken, wie z.B. für die (globale) Zustandsverwaltung (State-Management) und das Routing. Redux und React Router sind entsprechende Beispiele für solche Bibliotheken. [1]

Unidirektionaler Datenfluss

Anders als UI-Frameworks wie Angular verzichtet React bewusst auf eine bidirektionale Datenbindung. An deren Stelle tritt der unidirektionale Datenfluss, bei dem Daten stets nur in einer Richtung übergeben werden.

Was zunächst nach einer gravierenden Einschränkung klingt, stellt sich tatsächlich als Bereicherung dar. Der unidirektionale Datenfluss erhöht nämlich die Nachvollziehbarkeit, wie Daten innerhalb der Anwendung verteilt und verarbeitet werden. Das erleichtert die Analyse und bei Bedarf die Fehlersuche.


Um Reacts Konzept des unidirektionalen Datenflusses zu unterstützen, wurde die Flux-Architektur als Alternative zur beliebten Model-View-Controller-Architektur entwickelt. Mehr dazu im Detail wird in einem unterem Kapitel beschrieben.

Komponenten

React-Komponenten sind kleine, wiederverwende-bare Codestücke, die ein zu renderndes React-Element zurückgeben. [2] Komponenten werden in React hierarchisch aufgebaut und können in dessen Syntax als selbst definierte HTML-Tags repräsentiert werden.

Komponenten können in mehrere kleinere Teile zerlegt und deren Funktionialität in anderen Komponenten wiederverwendet werden. Komponenten können andere Komponenten, Arrays, Strings und Nummern zurückgeben.

Properties

Vom Konzept her sind Komponenten wie JavaScript-Funktionen. Sie akzeptieren beliebige Eingaben (readonly “props” genannnt) und geben React-Elemente zurück, welche beschreiben was auf dem Bildschirm angezeigt werden soll.

React-Komponente mithilfe von Javascript Funktionen definieren

Der einfachste Weg eine Komponente zu definieren, ist eine JavaScript-Funktion zu schreiben:

function Welcome(props) {
  return <h1>Hallo, {props.name}</h1>;
}

React-Komponente mithilfe von Javascript Klassen definieren

Komponenten können ebenso als ES6-Klassen definiert werden:

class Welcome extends React.Component {
  render() {
    return <h1>Hallo, {this.props.name}</h1>;
  }
}


Kritik am Komponenten-zentrierten Modell von React

Anders als bei strikten MVC-Modellen wird in React ein komponentenzentriertes Modell vorgeschlagen, welches Logik für Interaktion und Darstellung innerhalb eines Objekts bündelt (Seperation of Concerns). Dies wird insbesondere aufgrund der weitverbreiteten strikten Trennung zwischen Markup und Logik in Form von Templating-Systemen oft kritisch gesehen. [3]

Darstellungselemente (!= Komponenten)

React-Elemente sind die Bausteine von React-Anwendungen. Elemente könnten mit dem allgemein bekannteren Konzept der “Komponenten” verwechselt werden. Ein Element beschreibt was man auf dem Bildschirm sehen möchtest. React-Elemente sind unveränderbar.

Normalerweise werden Elemente nicht direkt verwendet, sondern von Komponenten zurückgegeben. [2]

const element = <h1>Hallo, Welt</h1>;

Anders als die DOM Elemente eines Browsers, sind React Elemente schlichte kosten-effektive Objekte. React DOM kümmert sich um das Aktualisieren des DOMs und den dazugehörigen React Elementen.

Virtual DOM und DOM-Diffing

Offizielles React-Beispiel zum Thema DOM-Diffing

Die Kernidee von React besteht in der Annahme, dass der komplette untergeordnete Anwendungsbaum einer betroffenen Komponente bei jeder Änderung einer Eigenschaft dieser Komponente neu aufgebaut wird. Da es in der Praxis rechenintensiv sein kann, dies z. B. im Webbrowser innerhalb des DOM durchzuführen, wurde das Konzept des „Virtual DOM“ geschaffen. [3]


Das virtuelle DOM (VDOM) ist ein Programmierkonzept, bei dem eine ideale oder “virtuelle” Darstellung der Benutzerschnittstelle (UI) im Speicher gehalten und mit dem “echten” DOM mittels einer Bibliothek names ReactDOM synchronisiert wird.

Die damit verbundene Technik des „DOM-Diffing“ beschreibt das selektive Aktualisieren des DOM auf Basis eines Vergleichs zwischen ursprünglichem und geändertem Virtual DOM. Siehe hierzu React's Dokumentations-Eintrag zum Thema "Vergleichsalgorithmus (Reconciliation)".

JavaScript Syntax Extension (JSX)

Mit Hilfe der an XML angelehnten Template-Sprache JSX (Javascript XML) steht ein optionaler Syntax für die Deklaration von React-Komponenten zur Verfügung, die es erlaubt, Javascript-Logik, HTML und CSS in eine React-Komponente einzukapseln und modular in Web-Applikationen einzusetzen.

JSX an-sich ist eine Syntaxerweiterung für JavaScript. Es ist ähnlich einer Template-Sprache, hat aber den vollen Leistungsumfang von JavaScript. [2]

Warum JSX

React lebt den Fakt, dass die Logik des Renderings und andere UI-Logiken grundsätzlich miteinander verbunden sind: Wie Events behandelt werden, wie sich der State über die Zeit verändert und wie Daten für die Darstellung vorbereitet werden.

Anstelle von künstlich separierten Technologien, bei denen Markup und Logik in getrennten Dateien liegen, trennt React die Dateien nach Zuständigkeit (Seperation of Concerns) auf und bindet die daraus resultierenden Einheiten in sogenannten Komponenten (“components”) lose zusammen.

Diese Komponenten enthalten sowohl Markup als auch Logik.

JSX ist auch ein Ausdruck

Jedes JSX Element ist eigentlich nur “syntactic sugar” für den Aufruf von React.createElement(component, props, ...children).

m Grunde-genommen benötigt man also kein kein JSX für React. Die Meisten empfinden es aber als hilfreiches Werkzeug, wenn sie in JavaScript-Code an der UI arbeiten. Zusätzlich bietet es React die Möglichkeit, bessere Fehlermeldungen und Warnungen anzuzeigen.

Siehe folgende Tabelle als Beispiel:

Originaler JSX-Code Mit Babel kompilierter JavaScript-Code (Siehe den offiziellen & interaktiven Online Babel-Compiler um "eigene Beispiele zu testen")
 1 class Hello extends React.Component {
 2   render() {
 3     return <div>Hello {this.props.toWhat}</div>;
 4   }
 5 }
 6 
 7 ReactDOM.render
 8   <Hello toWhat="World" />,
 9   document.getElementById('root')
10 );
1 class Hello extends React.Component {
2   render() {
3     return /*#__PURE__*/React.createElement("div", null, "Hello ", this.props.toWhat);
4   }
5 }
6 
7 ReactDOM.render( /*#__PURE__*/React.createElement(Hello, {
8   toWhat: "World"
9 }), document.getElementById('root'));

Wie man sieht werden nach dem Kompilieren JSX Ausdrücke als normale JavaScript-Funktionssaufrufe, die JavaScript-Objekte zurückgeben, ausgewertet.

Das bedeutet, dass man JSX innerhalb von if-Blöcken und for-Schleifen verwenden, Variablen zuweisen, als Argument entgegennehmen oder aus einer Funktion zurückgeben kann:

function getGreeting(user) {
  if (user) {
    return <h1>Hallo {formatName(user)}!</h1>;
  }
  return <h1>Hallo Fremder.</h1>;
}

JavaScript-Ausdrücke in JSX

Jeder valide JavaScript Ausdruck ist zwischen den geschweiften Klammern in JSX erlaubt. Zum Beispiel sind 2 + 2, user.firstName, oder formatName(user) vollkommen valide JavaScript-Ausdrücke.

const name = 'Arthur C Clarke';
const element = <h1>Hallo {name}</h1>;

ReactDOM.render(
 element,
 document.getElementById('root')
);

Attribute in JSX festlegen

Attribute eines Tags/Elements werden wie in HTML/XML definiert, mit der Ausnahme dass man entweder String-Literale als Wert oder auch jeden validen JavaScript-Ausdruck mithilfe von den genannten Schweiften Klammern angeben kann:

// prop-Angabe mit einem String-Literal
const element = <div tabIndex="0"></div>;
// prop-Angabe mit einem JavaScript-Ausdruck
const element = <img src={user.avatarUrl}></img>;

Folgende Regeln gilt es zu beachten:

  • Setze keine Anführungszeichen um geschweifte Klammern um JavaScript-Ausdrücke in ein Attribut einzubinden. Benutze entweder Anführungszeichen (für Strings) oder geschweifte Klammern (für Ausdrücke) aber nicht beides zusammen im selben Attribut.
  • Da JSX näher an JavaScript als an HTML ist, verwendet React DOM camelCase als Namenskonvention für Eigenschaften anstelle der für HTML typischen schreibweise.
    • class wird in JSX zum Beispiel zu className, und tabindex zu tabIndex.


Kind-Elemente mit JSX angeben

Ist ein Element leer, kannst du es wie in XML mit /> schließen:

const element = <img src={user.avatarUrl} />;

JSX-Tags können Kind-Elemente enthalten:

const element = (
  <div>
    <h1>Hallo!</h1>
    <h2>Schön dich hier zu sehen.</h2>
  </div>
);

children prop

In den React-Dokumenten heißt es, dass props.children für Komponenten verwendet werden können, die "allgemeine Schachteln" darstellen und die "ihre Kinder-Elemente nicht im Voraus kennen". Das heißt dass eine Komponente im Normalfall keine Annahmen darüber machen sollte, was für ein Wert in props.children gefüttert wird - sie soll es einfach nur einbauen.

Im folgenden Beispiel bekommt die h1-Komponente 2 props übergeben:

  • className mit dem Wert "greeting" (String-Literal)
  • children mit dem Wert "Hallo Welt!" (String-Literal)
const element = (
  <h1 className="greeting">
    {/* Hier kann so gut wie alles drinnen stehen. (JavaScript-Ausdrücke die React-Elemente wiedergeben, ...)*/}
    Hallo Welt!
  </h1>
);

Im Prinzip entsteht daraus das folgende Objekt:

// Hinweis: Dies ist eine vereinfachte Struktur
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hallo Welt!'
  }
};


JSX verhindert Injektionsangriffe

Standardmäßig escaped React DOM jeden in JSX eingebetteten Wert vor dem Darstellen. Damit wird sichergestellt, dass niemals etwas in die Anwendung gelangt, dass nicht explizit so implementiert wurde.

Alles wird zu einem String konvertiert und danach erst gerendert. Das hilft XSS (cross-site-scripting)-Attaken vorzubeugen.


Komponenten-State und Lifecycle


Event Handhabung

Die Handhabung von Events in React-Elementen ist ähnlich wie bei DOM-Elementen. Es gibt nur ein paar syntaktische Unterschiede:

  • Events in React werden nicht in Kleinbuchstaben, sondern in camelCase benannt.
  • In JSX übergibt man eine Funktion als Eventhandler anstatt als String.

Ein weiterer Unterschied ist, dass false nicht zurückgegeben werden kann, um das Standardverhalten von React zu unterbinden. Es muss explizit preventDefault aufgerufen werden. Um beispielsweise das Aufrufen eines Links in einfachem HTML zu verhindern.

HTML React (mit JSX)
<button onclick="activateLasers()">
  Aktiviere Laser
</button>
<button onClick={activateLasers}>
  Aktiviere Laser
</button>
<a href="#" onclick="console.log('Der Link wurde geklickt.'); return false">
  Klick mich
</a>
function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('Der Link wurde geklickt.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Klick mich
    </a>
  );
}

Im zweiten Beispiel ist e ein synthetisches Event. React definiert diese synthetischen Events gemäß der W3C spec, weswegen man sich keine Sorgen über browserübergreifende Kompatibilität machen muss. React-Events arbeiten genauso wie native Events. Mehr Informationen findet man unter der API-Referenz zu SyntheticEvent.

Mit React sollte man im Normalfall addEventListener nicht aufrufen müssen, um Events an DOM Elemente zu binden, nachdem sie erstellt wurden. Stattdessen stellt man einfach einen Listener zur Verfügung, wenn das Element initial gerendert wurde.

Argumente an Eventhandler übergeben

Innerhalb einer Schleife ist es üblich, einen zusätzlichen Parameter an den Eventhandler zu übergeben. Wenn beispielsweise id die ID einer Zeile ist, würde folgendes funktionieren:

<button onClick={(e) => this.deleteRow(id, e)}>Zeile entfernen</button>
<button onClick={this.deleteRow.bind(this, id)}>Zeile entfernen</button>

Die beiden obigen Zeilen sind äquivalent und benutzen Lambda-Funktionen bzw. Function.prototype.bind. In beiden Fällen repräsentiert das e Argument das React Event und wird als zweites Argument nach der ID mitgeliefert. Bei einer Lambda-Funktion müssen wir es explizit übergeben, aber mit bind werden alle weiteren Argumente automatisch weitergeleitet.


Bedingte Darstellung

Die bedingte Darstellung (engl. conditional rendering) in React funktioniert genauso wie Bedingungen in JavaScript.

Ein einfacher Weg bedingte Darstellung zu erzielen ist mithilfe der Nutzung von if-Blöcken wie folgt realisierbar.

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  if (isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}

ReactDOM.render(
  // Probiere mal isLoggedIn={true} aus:
  <Greeting isLoggedIn={false} />,
  document.getElementById('root')
);

Während if Ausdrücke einen guten Weg darstellen um eine Komponente bedingt anzuzeigen, benötigt man manchmal eine etwas kürzere Schreibweise. Es gibt mehrere Wege, Bedingungen direkt in JSX mit aufzunehmen:


Bedingung in JSX mit dem logischen && Operator (nicht-empfohlen)

Es können beliebige Ausdrücke in JSX verwendet werden, indem man sie in geschweifte Klammern schreibt. Das beinhaltet auch den logischen && Operator aus JavaScript:

function Mailbox(props) {
  const unreadMessages = props.unreadMessages;
  return (
    <div>
      <h1>Hallo!</h1>
      {unreadMessages.length > 0 &&
        <h2>
          Du hast {unreadMessages.length} ungelesene Nachrichten.
        </h2>
      }
    </div>
  );
}

const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
  <Mailbox unreadMessages={messages} />,
  document.getElementById('root')
);

Das funktioniert, weil true && ausdruck in JavaScript immer zu ausdruck evaluiert und false && ausdruck immer zu false evaluiert.

Ein ggf. ungewolltes Verhalten darf aber nicht übersehen werden: Wenn die Bedingung fehlschlägt wird die Zahl 0 ins JSX-Element eingebettet und somit ge-rendert (Da der JavaScript-Asudruck false zurückgegeben hat, und false der Zahl 0 entspricht)!! Keine Sorge, selbst ein PayPal-Entwickler hatte diesen Fehler gemacht.

Ein noch schlimmeres Verhalten tritt auf wenn man als Bedingung ein Objekt aus seine Wahrheit prüft, das undefined sein kann.

function Error({error}) {
  return error && <div className="fancy-error">{error.message}</div>
}

Dieser Code führt zu einem (ggf. nicht-selbst-behandelten) Runtime-Fehler, da undefined && anything immer undefined zurückgibt!:

Uncaught Error: Error(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.

In JSX eingebettetes If-Else mit Ternären-Operator

Eine andere Methode wie man Elemente bedingt in JSX eingebettet darstellen kann, ist die Nutzung des bedingten Operators bedingung ? true : false aus JavaScript.

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      Der Benutzer ist <b>{isLoggedIn ? 'zur Zeit' : 'nicht'}</b> eingeloggt.
    </div>
  );
}

Der Operator kann auch für größere Ausdrücke genutzt werden, wobei es dann schwerer nachzuvollziehen wird was passiert:

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      {isLoggedIn
        ? <LogoutButton onClick={this.handleLogoutClick} />
        : <LoginButton onClick={this.handleLoginClick} />
      }
    </div>
  );
}

Darstellung einer Komponente unterbinden

In seltenen Fällen möchtest du womöglich eine Komponente ausblenden, obwohl sie von einer anderen Komponente dargestellt wird. Dafür kannst du null anstelle des Inhalts als Ausgabewert zurückgeben. (NICHT undefined!)

Listen und Keys

Wie bereits in der Einführung von React-Komponenten beschrieben wurde, kann JSX auch Arrays von renderbaren-Objekten rendern. (D.h. primitive Datentypen und React-Elemente)

Logisch-erweiße hat man natürlich keine Liste an React-Elementen abgespeichert, sondern eine Liste mit Daten. Um Listen zu transformieren benützt man in Javascript die .map()-Funktion. So wird im nächsten Beispiel eine Liste aus Zahlen in eine Liste aus React-Elementen transformiert und diese an zum rendern übergeben.

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li>{number}</li>
);
ReactDOM.render(
  <ul>{listItems}</ul>,
  document.getElementById('root')
);


Schlüssel

Schlüssel (im Weiteren Keys genannt) helfen React zu erkennen, welche Elemente geändert, hinzugefügt oder gelöscht wurden. Keys sollten Elementen in einem Array zugewiesen werden, um diesen eine beständige Identität zu geben.

Der beste Weg einen Key zu definieren, ist die Verwendung eines Strings, der das Listelemente eindeutig von seinen Geschwisterelementen unterscheiden lässt.

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);

Wenn du keine IDs für die gerenderten Elemente hast, kannst du als letzte Maßnahme auch den Listenindex benutzen.

Es wird dringendst empfohlen, nicht die Indizes für die Keys zu verwenden, da sich die Reihenfolge der Listeneinträge verändern können. Dies kann sich negativ auf die Performance auswirken und zu Problemen mit dem Komponenten-State führen. [4]

Falsche Verwendung von Keys

Keys ergeben nur im Zusammenhang mit einem umgebenden Array Sinn.

Wenn du beispielsweise eine ListItem-Komponente extrahierst, solltest du den Key auf das <ListItem />-Element im Array setzen und nicht auf das <li>-Element im ListItem selbst.

function ListItem(props) {
  const value = props.value;
  return (
    // Falsch! Die Angabe des Keys ist hier nicht erforderlich:
    <li key={value.toString()}>
      {value}
    </li>
  );
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    // Falsch! Der Key sollte hier angegeben werden:
    <ListItem value={number} />
  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

Keys müssen nur bei Geschwistern eindeutig sein

Keys, die in einem Array verwendet werden, sollten eindeutig unter ihren Geschwistern sein. Global müssen sie dies jedoch nicht.

Einbetten von map() in JSX

JSX erlaubt es, jeden Ausdruck, der in geschweifte Klammern gesetzt wird, einzubetten. Somit kann man map() auch inline setzen:

function NumberList(props) {
  const numbers = props.numbers;
  return (
    <ul>
      {numbers.map((number) =>
        <ListItem key={number.toString()}
                  value={number} />
      )}
    </ul>
  );
}

Manchmal führt dies zu klarerem Code, aber dieser Stil kann ebenso missbraucht werden. Wie in JavaScript liegt es an dir zu entscheiden, ob es sich lohnt, eine Variable zur besseren Lesbarkeit zu benutzen. Behalte im Hinterkopf, dass wenn map() zu verschachtelt ist, es wahrscheinlich eine guter Zeitpunkt ist, diesen Code in eine Komponente auszulagern.

Formulare


State anheben

Komposition vs Vererbung


React Einführung - Weiteres

Flux-Architektur

Visuelle Darstellung des unidirektionalen Datenflusses der Flux-Architektur zwischen Ihren 4 Hauptkomponenten in Verbindung mit einer Webseite. Quelle: Medium

Um Reacts Konzept des unidirektionalen Datenflusses zu unterstützen, wurde die Flux-Architektur als Alternative zur beliebten Model-View-Controller-Architektur entwickelt.


Flux bietet

  • Aktionen (Actions), die über einen
  • zentralen Dispatcher an einen
  • Speicher (Store) gesendet werden, und Änderungen am Speicher (Store) werden zurück an die
  • Ansicht (View) propagiert.

Bei der Verwendung mit React wird diese Propagierung durch das Übergeben von Komponenteneigenschaften (Props) bewerkstelligt.


Seit seiner Vorstellung wurde das Flux-Konzept von Bibliotheken wie Redux und MobX abgelöst.

Die 4 Komponenten einer Flux-Architektur im Detail

Die Flux-Architektur baut sich aus den 4 folgenden Elementen zusammen:

  • Actions (Aktionen): Ein Objekt mit Eigenschaften und Daten, dessen Aufgabe es ist, das Geschehene zu beschreiben. Beispiel: Eine Aktion, die beschreibt das ein Benutzer einen anderen Benutzer "gefolgt" hat, kann z.B. eine Benutzerkennung (ID), eine Zielbenutzerkennung (ID) und den Typ USER_FOLLOWED_ANOTHER_USER enthalten.
  • Stores (Speicher): Enthält den Status und die Logik der Anwendung. Die Stores, die man sich als Modelle vorstellen kann, können sich als Reaktion auf die vom Dispatcher erhaltenen ''Aktionen'' selbst ändern.
  • Dispatcher (Disponent): Verarbeitet registrierte Aktionen und Callbacks.
  • Views (Ansichten): Hören sich die Änderungen vom Store an und rendern sich selbst erneut.

Eine React-Komponente unter der Flux-Architektur sollte keine direkt an sie übergebenen Props (Eigenschaften/Werte) modifizieren, sondern es sollten Callback-Funktionen übergeben werden, die Aktionen erzeugen, die vom Dispatcher gesendet werden, um den ''Store'' zu modifizieren.

React Einführung - React-App-Entwicklung

Offiziell Vorgeschlagene Möglichkeiten zur Erstellung einer React-Anwendung

Das React Team empfiehlt bevorzugt diese Lösungen: [5]

  • Wenn man React lernt oder eine neue Single Page Anwendung erstellen will, hilft einem Create React App.
    • Create React App ist ein Open-Source npm-Skript dass einem automatisch eine vollständig funktionsfähige Entwicklungsumgebung für neue React-SPAs aufsetzt. Die verwendeten Entwicklungs-Toolchains sind der Compiler Babel und der Bundler webpack.
  • Wenn du eine vom Server gerenderte Webseite mit Node.js baust, dann probier Next.js.
    • Next.js ist ein beliebtes und leichtgewichtiges Framework für statische und server-gerenderte Anwendungen, die mit React erstellt wurden. Es enthält sofort einsatzbereite Styling- und Routing-Lösungen und setzt voraus, dass man Node.js als Serverumgebung verwendet.
  • Wenn du eine statische auf Inhalt orientierte Webseite baust, dann probier Gatsby.
    • Gatsby ist der beste Weg, statische Websites mit React zu erstellen. Es ermöglicht die Verwendung von React-Komponenten, gibt aber vorgerendertes HTML und CSS aus, um die schnellste Ladezeit zu garantieren.
  • Wenn du eine Komponentenbibliothek baust oder mit einer existierenden Codebasis integrierst, dann probiere flexiblere Werkzeuge:


Offizielle Tutorials

ReactJS's Webseite bietet ein Haufen an Dokumentationsmaterialien, darunter auch Anleitungen für Einsteiger und über Fortgeschrittenere Themen.

Das Tutorial ist für Leute gedacht, die die "Praktible / Learning by Doing"-Lernmethode bevorzugen. Hier wird man in React eingeführt indem einem zuerst die Grundprinzipien von React erläutert werden und danach Schritt für Schritt ein Tic-Tac-Toe-Spiel entwickelt wird.

Weiterführend gibt es auch noch eine "Step by Step"-Anleitung, indem einem die Grundkonzepte  sowie auch folgend Fortgeschrittenere Themen nacheinander erläutert werden. Natürlich ist auch dies kein "trockenes Lesen": So gut wie nach jedem Thema (oder nachdem etwas mit Code erklärt wurde) gibt es einen oder mehrere Links zu einer entsprechenden "Try it on CodePen"-Seite, auf der man den Code spielerisch leicht laufen lassen und live verändern kann.

Eine simple React-App-Entwicklungsumgebung Schritt-für-Schritt selbst aufsetzen

Quellen (Ich bin schlecht in Schritt-für-Schritt Tutorials ohne zu viele Kenntnisse vorauszusetzen): "Creating a React-App.. From Scratch" (Medium), "Creating a New React App" (React-Doc), "Using React with Grails" (ObjectComputing)


React funktioniert nicht "einfach so". Es verwendet Schlüsselwörter und einen Syntax mit dem NodeJS nichts anfangen kann (z.B. die Schlüsselwörter import und export, oder der JSX-Syntax). Es erfordert eine ziemlich umständliche Einrichtung, und Facebook hat mit "Create-React-App" eine Option bereitgestellt, die das Starten einer React-Anwendung einfach macht.

Warum sich dann die Mühe machen? Die Sache ist die, dass "Create-React-App" vieles von dem abstrahiert, was eine "React-App" überhaupt im Browser funktionieren lässt. Es gibt eine Reihe von Gründen warum man vielleicht eine eigene Implementierung machen will, oder zu-mindestens verstehen will welche Komponente im Prozess für was eigentlich zuständig ist.

Bestandteile einer typischen JavaScript Build Werkzeugkette

Node.js Logo

Typischerweise besteht eine JavaScript Build Werkzeugkette aus:

  • Einem Paket Manager wie Yarn oder npm, der einem Zugriff auf ein breites Ökosystem von Drittbibliotheken zur Verfügung stellt die man einfach installieren oder aktualisieren kann.
  • Einem Bundler wie webpack oder Parcel, der es einem erlaubt, modularen Code zu schreiben und ihn in kleine Pakete (Bundles) zu packen, unter anderem um die Ladezeit zu optimieren.
  • Einem Compiler/Transpiler wie Babel erlaubt es einem, Modernen JavaScript Code zu schreiben und ihn in Code umzuwandeln ("polyfill"en), den auch ältere Browser verstehen können.


Setup

Erstelle ein neues Verzeichnis für die React-Anwendung und öffne diesen Ordner in einem Terminal.

Diesen Ordner markieren wir als den Root-Pfad für eine neue Node-Anwendung mit dem Befehl npm init.

  • Falls man keine aktuelle Version der Node/NPM-SDK auf seinem System installiert hat, empfehle ich dringend nvm (Es ist wie sdkman, aber für node/npm - Ein Tool zum installieren/managen von SDKs mithilfe einer einfach Kommandozeilenumgebung).


Nun erstellt man im Projektordner folgende Struktur:

.
+-- public
  +-- index.html
+-- src

Unser public-Verzeichnis verwaltet alle statischen Assets und beherbergt vor allem unsere index.html-Datei, die als Einstiegspunkt unserer Anwendung für den Browser dient. Sie sieht wie folgt aus:

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8" />
 5     <!--
 6         The X-UA-Compatible meta tag allows web authors to choose what version of Internet Explorer the page should be rendered as.
 7         According to Microsoft, when using the X-UA-Compatible tag, it should be as high as possible in your document head.
 8 
 9         "Edge" mode tells Internet Explorer to display content in the highest mode available. With Internet Explorer 9, this is equivalent to IE9 mode.
10 
11         See https://stackoverflow.com/questions/6771258/what-does-meta-http-equiv-x-ua-compatible-content-ie-edge-do
12     -->
13     <meta http-equiv="X-UA-Compatible" content="IE=edge">
14     <!--
15       Advised tag as described in https://material-ui.com/getting-started/usage/#responsive-meta-tag (and many other sources)
16       to ensure proper rendering and touch zooming for all devices:
17     -->
18     <meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width"/>
19     <title>React Starter</title>
20 </head>
21 
22 <body>
23     <!-- This will serve as the top-level-entry-point/container in which our React-Elements will hook into. -->
24     <div id="root"></div>
25     <noscript>
26     You need to enable JavaScript to run this app.
27     </noscript>
28     <!-- This will load/execute our "Plain Vanilla Javascript"-File that has previously been bundled & transpiled to be able to run by an browser (built) by us. -->
29     <script src="../dist/bundle.js"></script>
30 </body>
31 </html>

Jetzt, wo wir unsere HTML-Seite eingerichtet haben, können wir anfangen, ernsthaft zu werden. Zunächst müssen wir sicherstellen, dass der Code, den wir schreiben, kompiliert werden kann.

Installieren der benötigten NPM-Pakete

npm (Node Package Manager) LOGO

Natürlich brauchen wir React, und ReactDOM um unsere Anwendung im Browser zu rendern (React kann auch nativ auf mobilen Plattformen wie iOS verwendet werden, wobei ReactNative anstelle von ReactDOM verwendet wird).

# Dependencies used by the actual code/app (--save)
npm install react react-dom \
    --save

# Dependencies used for development purposes (--save-dev)
# babel-preset-react will be responsible to transpile all react-syntax (including JSX)
# babel-preset-es2015 will be responsible for transpiling the transpiled code from the previous preset so it can work on es2015-compatible-browsers
npm install babel-core babel-preset-es2015 babel-preset-react babel-cli \
    webpack webpack-cli babel-loader style-loader css-loader \
    --save-dev

Babel

BabelJS Logo

Da wir zum Schreiben unserer Komponenten JSX verwenden werden, müssen wir außerdem einen Transpiler verwenden, um unseren React-Code in "Plain Vanilla"-Javascript für den Browser umzuwandeln. Moderne Javascript-Frameworks bewegen sich viel schneller als Web-Browser, und viele der Javascript-APIs die häufig in React-Projekten verwendet werden sind noch nicht Standard in Browsern. babel ist ein sehr beliebter und einfach-erweiterbarer Transpiler.

Da Babel sehr vielseitig ist müssen wir eine kleine Grundkonfiguration einrichten. Ohne Plugin versteht der babel-Transpiler auch nur so-gut-wie Plain-JavaScript Syntax. Damit babel auch neuere Versionen von JavaScript sowie den JSX-Syntax verstehen und transformieren kann, müssen wir entsprechende Plugins hinzufügen.

Die Babel-Konfiguration wird vorzugsweise in der Datei .babelrc im Stammverzeichnis des Node-Projekts vorgenommen. Sie kann wie folgt aussehen:

{
  "presets": [
    "react",
    "es2015"
  ]
}

Webpack

Webpack Logo

Bundling ist der Prozess, bei dem importierte Dateien verfolgt und zu einer einzigen Datei zusammengeführt werden: einem "Bündel". Dieses Bundle kann dann auf einer Webseite eingebunden werden, um eine ganze Anwendung auf einmal zu laden. Abhängigkeiten werden während der Kompilierung aufgelöst, wodurch die Laufzeitgröße reduziert wird.

Betrachte folgendes Beispiel:

Originaler Code Beispielhafte Darstellung des resultierenden Bundels
1 // app.js
2 import { add } from './math.js';
3 
4 console.log(add(16, 26)); // 42
1 // math.js
2 export function add(a, b) {
3   return a + b;
4 }
1 function add(a, b) {
2   return a + b;
3 }
4 
5 console.log(add(16, 26)); // 42

Webpack ist ein sehr mächtiges, hoch-modulares Werkzeug um solche Bündel zu erstellen. Loader kümmern sich um die Kompilierung bestimmter Dateitypen für webpack. babel-loader ist zum Beispiel ein loader für JavaScript-Dateien.

Erstelle eine neue Datei im Stammverzeichnis des Projekts mit dem Namen webpack.config.js. Diese Datei exportiert ein Objekt mit der Konfiguration von webpack.

// As this is run by NodeJS, it only supports CommonJS Syntax for importing
const path = require("path");
const webpack = require("webpack");

module.exports = {
  // tells Webpack where our application starts and where to start bundling our files.
  entry: "./src/index.js",
  // The following line lets webpack know we’re working in development mode 
  // — This saves us from having to add a mode flag when we run the development server.
  mode: "development",
  
  // The module object helps define how your exported javascript modules are transformed and which ones are included according to the given array of rules.
  module: {
    rules: [
      // Our first rule is all about transforming our ES6 and JSX syntax. 
      {
        // The test and exclude properties are conditions to match file against. In this case, it’ll match anything outside of the node_modules and bower_components directories.
        test: /\.(js|jsx)$/,
        exclude: /(node_modules|bower_components)/,
        // Since we’ll be transforming our .js and .jsx files as well, we’ll need to direct Webpack to use Babel. 
        // loader is a shorthand for the use property, when only one loader is being utilized.
        loader: "babel-loader",
        // Finally, we specify that we want to use the env preset in options.
        options: { presets: ['es2015', 'react'] }
      },
      // The next rule is for processing CSS. Since we’re not pre-or-post-processing our CSS, we just need to make sure to add style-loader and css-loader to the use property. css-loader requires style-loader in order to work. 
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"]
      }
    ]
  },
  // The resolve property allows us to specify which extensions Webpack will resolve — this allows us to import modules without needing to add their extensions.
  resolve: { extensions: ["*", ".js", ".jsx"] },
  // The output property tells Webpack where to put our bundled code. The publicPath property specifies what directory the bundle should go in.
  output: {
    path: path.resolve(__dirname, "dist/"),
    // If this is set incorrectly, you’ll get 404’s as the server won’t be serving your assets from the correct location!
    publicPath: "/dist/",
    filename: "bundle.js"
  },
};

NPM-Skripte erstellen

Für die Zwecke der Einfachheit werden wir auch ein paar "NPM Skripte" zu unserer Node-Datei package.json hinzufügen wollen, um webpack über npm auszuführen. Fügen Sie diese beiden Zeilen im Abschnitt "scripts" hinzu:

"scripts": {
    "watch": "webpack --watch --colors --progress",
    "bundle": "webpack",

Diese Skripte können nun auf folgende Weise ausgeführt werden: npm run [name]

Beim ausführen von npm run bundle werden Sie wahrscheinlich einen Fehler wie "Entry module could not be found." von webpack kriegen, weil unserer in webpack angegebener entry-point nicht existiert. Also, fangen wir endlich an React-Code zu schreiben:

Beispiel-Code

// /src/index.js

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

// ReactDOM.render is the function that tells React what to render and where to render it — In this case, we’re rendering a component called App (which we’ll create soon), and it’s being rendered at the DOM element with the ID "root".
ReactDOM.render(<App />, document.getElementById("root"));
// /src/App.js
// This file just holds a React component.

import React, { Component} from "react";
import "./App.css";

class App extends Component{
  render(){
    return(
      <div className="App">
        <h1> Hello, World! </h1>
      </div>
    );
  }
}

export default App;

Während wir noch hier sind, habe ich bereits erwähnt, dass Webpack auch CSS verarbeitet (und wir importieren es in unsere Komponente). Lassen Sie uns ein wirklich einfaches Stylsheet zum src-Verzeichnis hinzufügen:

// /src/App.css
// (kein statisches Asset im direkten Sinne - wird in die Javascript-Datei gebündelt und nur von dort aus erreichbar sein.)

.App {
  margin: 1rem;
  font-family: Arial, Helvetica, sans-serif;
}

Fertig

Nun können wir mithilfe von npm run bundle unseren Quellcode in eine einzelne, für den Browser verständliche und ausführbare Datei bündeln.

Jetzt sollten wir die index.html-Datei im Browser öffnen können und unsere Anwendung sollte erfolgreich geladen und angezeigt werden.


  1. Englischer Wikipedia-Eintrag zu "React"
  2. 2,0 2,1 2,2 Glossar der React Begriffe
  3. 3,0 3,1 Deutscher Wikipedia-Eintrag zu "React": Kritik
  4. Medium-Artikel von Robin Pokorny, der eine ausführlichere Erklärung über die negativen Auswirkungen beim Verwenden des Index als Key bietet: "(Why) Index as a key is an anti-pattern" (Wird sogar in der offiziellen React-Dokumentation verlinkt)
  5. React-Docs zu "Erstelle eine React-App"