Eine kontrollierte Komponente ist ein Formularelement, dessen Wert im React-State lebt: Du übergibst value und aktualisierst den State in onChange, also entscheidet React, was das Input zeigt. Eine unkontrollierte Komponente hält ihren Wert im DOM, wie reines HTML: Du übergibst einen Startwert defaultValue und liest den aktuellen Wert später über eine Ref oder FormData.
Tippe in beide Inputs. Der Absatz folgt dem ersten bei jedem Tastendruck, weil jede Änderung State setzt und erneut rendert. Das zweite Input ändert sich nur im DOM; React rendert nicht, und du siehst seinen Wert erst, wenn „Read both“ danach fragt.
Kontrollierte Inputs
Bei einem kontrollierten Input ist der React-State die einzige Quelle der Wahrheit. Der Ablauf bei jedem Tastendruck ist:
- Jemand tippt, und der Browser feuert ein Input-Event.
- Dein
onChange-Handler lieste.target.valueund ruft den Setter auf. - React rendert erneut und schreibt den neuen State zurück in den
valuedes Inputs.
Weil dein Code mitten in dieser Schleife sitzt, kannst du Dinge tun, die ein unkontrolliertes Input nicht kann: Zeichen ablehnen oder umformen, den Wert woanders anzeigen, einen Button deaktivieren, bis der Wert gültig ist, oder den Wert aus dem Code setzen (ein „Leeren“-Button, ein Vorschlag, den jemand anklickt).
Versuche, Buchstaben zu tippen: Sie erscheinen nie, weil der Handler alles entfernt, was keine Ziffer ist, bevor er State setzt. Der Button Verify bleibt deaktiviert, bis es genau sechs Ziffern sind.
Kontrolliert ist das, was die Formularbeispiele auf der Seite zu Formularen nutzen, und es ist in den meisten React-Codebasen die Standardwahl.
Unkontrollierte Inputs
Ein unkontrolliertes Input funktioniert wie reines HTML. React rendert es einmal mit defaultValue (oder defaultChecked für Checkboxen und Radios) und lässt es dann in Ruhe. Um den Wert zu lesen, hältst du entweder eine Ref auf das Element oder liest beim Absenden alle benannten Felder des Formulars mit FormData.
Es gibt keinen State für die Felder und kein onChange. Die Werte werden erst eingesammelt, wenn das Formular abgesendet wird. Entferne das Häkchen und sende erneut: Der Schlüssel subscribe verschwindet, weil eine nicht angehakte Checkbox gar nicht Teil von FormData ist.
defaultValue nach dem ersten Render zu ändern bewirkt am Input nichts, weil React es nur beim Erstellen des Elements anwendet. Um ein unkontrolliertes Formular zurückzusetzen, ruf e.target.reset() auf oder gib dem Formular einen neuen key, damit React frische Inputs erzeugt.
Ein Input, in das du nicht tippen kannst
Wenn du value übergibst, aber kein onChange, ist das Input kontrolliert, aber nichts aktualisiert je den State. React schreibt nach jedem Tastendruck denselben Wert zurück, also wirkt das Input eingefroren.
Tippe in jedes Feld. Das erste ändert sich nicht. Das zweite ist die Lösung für ein kontrolliertes Input, und während du dort tippst, folgen ihm das erste und das letzte Feld, weil alle drei denselben State lesen. Das dritte nimmt Eingaben an, weil es unkontrolliert ist. Das vierte sagt absichtlich, dass es nicht bearbeitet werden kann.
In der Entwicklung loggt React für das erste Input eine Warnung: „You provided a value prop to a form field without an onChange handler“. Die Vorschau läuft wie ein Produktions-Build, also siehst du diese Warnung hier nicht, aber auf deinem eigenen Dev-Server schon.
Von unkontrolliert zu kontrolliert wechseln
React entscheidet anhand von value, ob ein Input kontrolliert ist. Wenn value undefined oder null ist, ist das Input unkontrolliert. Ein Input, dessen value als undefined beginnt und später zu einem String wird, wechselt also im Laufe seines Lebens von unkontrolliert zu kontrolliert, und React warnt in der Entwicklung: „A component is changing an uncontrolled input to be controlled“.
Die Warnung zeigt auf einen echten Bug. Der umgekehrte Weg, von einem String zu undefined, macht das Input wieder unkontrolliert, und React hört auf, hineinzuschreiben.
Tippe etwas in beide Inputs und drück jeden Clear-Button. Das zweite Input wird leer. Das erste behält seinen Text: Den State auf undefined zu setzen hat es unkontrolliert gemacht, also bestimmt React nicht mehr, was es zeigt.
Die übliche Quelle für undefined sind Daten, die noch nicht geladen sind (value={user.name}, bevor der Nutzer da ist), oder ein Objektfeld, das es noch nicht gibt. Zwei Lösungen:
const [name, setName] = useState(''); // start with a string
<input value={user?.name ?? ''} /> // or fall back to one
Für Checkboxen gilt dieselbe Regel für checked: Starte mit false, nicht mit undefined.
Datei-Inputs sind immer unkontrolliert
Ein <input type="file"> kann nicht kontrolliert werden, weil Browser einer Seite nicht erlauben, seinen Wert zu setzen (eine Seite, die Dateien auf deiner Festplatte auswählen könnte, wäre eine Sicherheitslücke). Lies die gewählten Dateien in onChange aus e.target.files oder beim Absenden über eine Ref.
Wähle eine oder zwei Dateien von deinem Computer; nichts wird hochgeladen, das Beispiel listet nur Namen und Größen. Der einzige Wert, den du an einem Datei-Input setzen darfst, ist der leere String, und so setzt der Clear-Button es zurück.
Wann du was wählst
| Kontrolliert | Unkontrolliert | |
|---|---|---|
| Wert lebt in | React-State | Dem DOM |
| Startwert setzen | value / checked | defaultValue / defaultChecked |
| Wert lesen | Jederzeit, aus dem State | Mit einer Ref oder FormData beim Absenden |
| Rendert bei jedem Tastendruck | Ja | Nein |
| Wert aus dem Code ändern | State setzen | Über eine Ref ins DOM schreiben |
| Gut für | Live-Validierung, Formatierung, abhängige Felder | Einfache Formulare, die beim Absenden gelesen werden, Datei-Inputs, Form Actions |
Wähle kontrolliert, wenn der Bildschirm schon beim Tippen auf den Wert reagieren muss. Wähle unkontrolliert, wenn du die Werte erst am Ende brauchst, und so funktionieren auch die Form Actions in React 19: <form action={fn}> gibt deiner Funktion ein FormData und setzt danach die unkontrollierten Felder zurück.
Bei jedem Tastendruck zu rendern klingt teuer, ist für ein normales Formular aber kein Problem. Zum Problem wird es erst, wenn ein großer, langsamer Baum mit dem Input neu rendert; dann halte den State nah am Input oder verschiebe den langsamen Teil in eine eigene Komponente.
Dieselben zwei Wörter beschreiben auch deine eigenen Komponenten. Eine Komponente, die ihren Wert und ein onChange über Props von ihrem Elternteil bekommt, wird vom Elternteil kontrolliert; eine, die ihren eigenen State hält, ist unkontrolliert. State nach oben zu verschieben, um eine Komponente kontrolliert zu machen, ist das Thema von State nach oben verlagern.
Häufig gestellte Fragen
Was ist eine kontrollierte Komponente in React?
Ein Formularelement, dessen aktueller Wert aus dem React-State kommt, gesetzt mit value (oder checked) und aktualisiert über onChange. Der React-State ist die einzige Quelle der Wahrheit, also kennst du den Wert immer und kannst ihn aus dem Code ändern.
Was ist eine unkontrollierte Komponente in React?
Ein Formularelement, das seinen Wert selbst im DOM hält, wie reines HTML. Du gibst ihm mit defaultValue (oder defaultChecked) einen Startwert und liest den aktuellen Wert, wenn du ihn brauchst, über eine Ref oder FormData beim Absenden.
Warum kann ich in mein React-Input nicht tippen?
Du hast value ohne onChange übergeben. Das Input ist kontrolliert, also setzt React nach jedem Tastendruck wieder den Wert aus dem State ein. Füge ein onChange hinzu, das den State aktualisiert, wechsle zu defaultValue oder füge readOnly hinzu, wenn es sich wirklich nicht ändern soll.
Wie behebe ich „A component is changing an uncontrolled input to be controlled“?
Der value des Inputs war anfangs undefined (oder null) und wurde später zu einem String. Sorge dafür, dass er immer ein String ist: Starte den State mit '' oder schreib value={name ?? ''}.
Sollte ich kontrollierte oder unkontrollierte Inputs nutzen?
Nutze kontrollierte Inputs, wenn die UI schon beim Tippen auf den Wert reagiert: Validierung, Formatierung, ein deaktivierter Button, voneinander abhängige Felder. Nutze unkontrollierte Inputs, wenn du die Werte erst beim Absenden brauchst, und für Datei-Inputs, die immer unkontrolliert sind.