Formulare
Formulare besitzen in React eine kleine Sonderstellung und funktionieren etwas anders als andere DOM-Elemente, da Formulare eine Art eigenen State besitzen, der erst einmal nichts mit dem React-State gemein hat.
Der State von Textfeldern besteht bspw. aus dem eingegebenen Wert, der State von Checkboxen oder Radio-Buttons resultiert aus der Tatsache ob diese ausgewählt sind oder nicht, Auswahllisten (
<select></select>
) halten als State den ausgewählten Wert bzw. bei Mehrfachauswahl die ausgewählten Werte. React ändert an diesem Verhalten grundsätzlich erstmal nichts. Wer möchte, kann das so beibehalten und muss sich um nichts weiter kümmern. Im React-Jargon ist dann die Rede von Uncontrolled Components, also unkontrollierten Komponenten. Unkontrolliert deshalb, weil React sich nicht um das State-Management dieser Komponenten kümmert. Das State-Handling ist entweder vollständig unabhängig von React oder funktioniert nur aus Richtung der DOM Formular-Elemente hin zum React State, jedoch nicht in die entgegengesetzte Richtung. Von einem Update am React-State bekommt ein Formular-Element also nichts mit und zeigt weiter den gleichen Wert (oder Status bei Checkboxen, Selects und Radiobuttons) wie zuvor.
Demgegenüber stehen die Controlled Components, also kontrollierte Komponenten. Hier aktualisiert ein Update am React-State den Wert (oder Status) des Formular-Elements und ebenso aktualisiert ein Update am jeweiligen Formular-Element den React-State. Controlled Components sind etwas aufwändiger in der Implementierung, sind zugleich jedoch auch die „sicherere“ Variante, da wir nicht Gefahr laufen, dass beide States voneinander abweichen.
Unkontrollierte Komponenten können dabei im Wesentlichen in zwei verschiedenen Formen auftreten. Bei der ersten Variante werden einfach nur Formular-Elemente gerendert, die beim Abschicken bspw. rein serverseitig verarbeitet werden und in keiner Weise mit React interagieren. Ein komplett statisches Formular wenn man so will. React kümmert sich dabei nicht von alleine um die Anbindung an den React-State sondern lässt dem Entwickler hier sämtliche Freiheiten!
Bei der zweiten Variante werden Änderungen an einem Formular-Element in den React-State geschrieben, um bspw. im Hintergrund eine Validierung der Daten vorzunehmen oder die eingegebenen Daten an anderer Stelle auszugeben. Eine Änderung am React-State an anderer Stelle der Anwendung hat dabei keinerlei direkten Einfluss auf die Formularfelder.
Ein Beispiel für eine solche unkontrollierte Komponente:
class Uncontrolled extends React.Component {
state = {
username: '',
isValid: false,
};
changeUsername = e => {
const { value } = e.target;
this.setState(() => ({
username: value,
isValid: value.length > 3,
}));
};
submitForm = e => {
e.preventDefault();
alert(`Hallo ${this.state.username}`);
};
render() {
return (
<form method="post" onSubmit={this.submitForm}>
<p>Dein Benutzername: {this.state.username}</p>
<p>
<input
type="text"
name="username"
onChange={this.changeUsername} />
<input type="submit" disabled={!this.state.isValid} />
</p>
</form>
);
}
}
Hier sehen wir ein einfaches Textfeld, in das der Benutzer einen gewünschten Benutzernamen eintragen kann. Die
Uncontrolled
Komponente wird mittels onChange
-Event von jeder Änderung in Kenntnis gesetzt und kann den Benutzernamen weiterverarbeiten. Da React hier nur passiv agiert, also bei einer Änderung am Textfeld über den neuen Wert in Kenntnis gesetzt wird, bewegen wir uns immer noch im Bereich der Uncontrolled Components.Dies ist in einigen Fällen ausreichend, insbesondere wenn die Formulare noch nicht all zu komplex sind. Allerdings ist der React-State hier vom DOM State entkoppelt bzw. funktioniert nur in eine Richtung. Der React-State wird aktualisiert, sobald das
onChange
-Event des Textfelds ausgelöst wird. Allerdings bedeutet dies, dass nicht gleichzeitig auch unser Textfeld aktualisiert wird, wenn der Wert im React-State an anderer Stelle verändert wurde, bspw. weil der Response eines asynchronen Requests nach einiger Zeit eintrifft.Ein Formularfeld gilt als kontrolliert, sobald ein
value
-Attribut gesetzt wird. Ab diesem Moment erwartet React, dass wir uns als Entwickler selbst darum kümmern, den React-State mit dem Formularfeld zu synchronisieren. Möchten wir allerdings nur einmalig einen initialen Wert setzen ohne gleich die ganze Komponente zu einer Controlled Component zu machen, haben wir die Möglichkeit, statt des value
-Attributs das React eigene defaultValue
-Attribut zu setzen (defaultChecked
bei Checkboxen und Radiobuttons). Das Element bleibt dann weiterhin unkontrolliert, zeigt aber dennoch einen vorausgefüllten Wert (bzw. Status) an.Um sowohl State-Updates in Formularfeldern abzubilden als auch auf der anderen Seite benutzerseitige Änderungen an Formularfeldern in den React-State zu übertragen, benötigen wir eine Controlled Component. Hier überlassen wir das State-Handling eines Formular-Elements vollständig React. Dies bedeutet, dass wir das
value
-Attribut mit einem Wert befüllen, den wir aus dem React-State beziehen und gleichzeitig auch einen geänderten Wert wieder zurück in den React-State überführen.Das Ziel bei diesem Ansatz ist es, den React-State (oder einen anderen State-Container wie z.B. Redux) als Single Source of Truth zu betrachten, also als die einzige Quelle der Wahrheit. Relevant ist der Wert, der im von React verwalteten State steht, das jeweilige Eingabefeld reflektiert dann zu jedem Zeitpunkt den Wert aus diesem State.
Schauen wir uns auch hierzu mal ein Beispiel an:
class Controlled extends React.Component {
state = {
username: '',
isValid: false,
};