Reagieren - Ändern eines unkontrollierten Eingangs

360

Ich habe eine einfache Reaktionskomponente mit der Form, von der ich glaube, dass sie eine kontrollierte Eingabe hat:

import React from 'react';

export default class MyForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = {}
    }

    render() {
        return (
            <form className="add-support-staff-form">
                <input name="name" type="text" value={this.state.name} onChange={this.onFieldChange('name').bind(this)}/>
            </form>
        )
    }

    onFieldChange(fieldName) {
        return function (event) {
            this.setState({[fieldName]: event.target.value});
        }
    }
}

export default MyForm;

Wenn ich meine Anwendung ausführe, wird folgende Warnung angezeigt:

Warnung: MyForm ändert eine unkontrollierte Eingabe von zu steuerndem Text. Eingangselemente sollten nicht von unkontrolliert zu kontrolliert (oder umgekehrt) wechseln. Entscheiden Sie sich für die Lebensdauer der Komponente zwischen der Verwendung eines gesteuerten oder unkontrollierten Eingabeelements

Ich glaube, meine Eingabe wird gesteuert, da sie einen Wert hat. Ich frage mich, was mache ich falsch?

Ich benutze React 15.1.0

alexs333
quelle

Antworten:

510

Ich glaube, meine Eingabe wird gesteuert, da sie einen Wert hat.

Damit ein Eingang gesteuert werden kann, muss sein Wert dem einer Zustandsvariablen entsprechen.

Diese Bedingung ist in Ihrem Beispiel zunächst nicht erfüllt, da this.state.namesie zunächst nicht festgelegt ist. Daher ist die Eingabe zunächst unkontrolliert. Sobald der onChangeHandler zum ersten Mal ausgelöst wird, this.state.namewird gesetzt. Zu diesem Zeitpunkt ist die obige Bedingung erfüllt und die Eingabe wird als gesteuert betrachtet. Dieser Übergang von unkontrolliert zu kontrolliert erzeugt den oben gezeigten Fehler.

Durch Initialisierung this.state.nameim Konstruktor:

z.B

this.state = { name: '' };

Die Eingabe wird von Anfang an gesteuert, um das Problem zu beheben. Siehe Reagieren kontrollierter KomponentenWeitere Beispiele finden .

Unabhängig von diesem Fehler sollten Sie nur einen Standardexport haben. Ihr Code oben hat zwei.

fvgs
quelle
7
Es ist schwierig, die Antwort zu lesen und der Idee oft zu folgen, aber diese Antwort ist die perfekte Art, Geschichten zu erzählen und den Betrachter gleichzeitig verständlich zu machen. Antworte Level Gott!
surajnew55
2
Was ist, wenn Sie dynamische Felder in einer Schleife haben? zB setzen Sie den Feldnamen auf name={'question_groups.${questionItem.id}'}?
user3574492
2
Wie würden dynamische Felder funktionieren? Generiere ich das Formular dynamisch und setze dann die Feldnamen in einem Objekt innerhalb des Status. Muss ich die Feldnamen immer noch zuerst manuell in den Status setzen und sie dann auf mein Objekt übertragen?
Joseph
13
Diese Antwort ist etwas falsch. Eine Eingabe wird gesteuert, wenn die valueRequisite einen Wert ungleich Null / undefiniert hat. Die Requisite muss keiner Zustandsvariablen entsprechen (sie kann nur eine Konstante sein und die Komponente wird weiterhin als gesteuert betrachtet). Adams Antwort ist korrekter und sollte akzeptiert werden.
ecraig12345
2
Ja - das ist technisch die falsche Antwort (aber hilfreich). Ein Hinweis dazu - Wenn Sie es mit Optionsfeldern zu tun haben (deren 'Wert' etwas anders gesteuert wird), kann diese Reaktionswarnung tatsächlich ausgelöst werden, selbst wenn Ihre Komponente (derzeit) gesteuert wird. github.com/facebook/react/issues/6779 und kann durch Hinzufügen eines !! zur wahrheit von isChecked
mheavers
123

Wenn Sie Ihre Komponente zum ersten Mal rendern, this.state.nameist sie nicht festgelegt, sodass sie als undefinedoder ausgewertet nullwird und Sie am Ende value={undefined}oder value={null}an Ihre übergeben input.

Wenn ReactDOM prüft, ob ein Feld gesteuert wird, prüft es, ob es vorhanden istvalue != null (beachten Sie !=, dass dies nicht der!== ), und undefined == nullentscheidet in JavaScript, dass es nicht kontrolliert wird.

Wenn onFieldChange()also aufgerufen this.state.namewird, auf einen Zeichenfolgenwert gesetzt wird, geht Ihre Eingabe von unkontrolliert zu kontrolliert über.

Wenn Sie dies this.state = {name: ''}in Ihrem Konstruktor tun '' != null, hat Ihre Eingabe die ganze Zeit über einen Wert, und diese Nachricht verschwindet.

Leigh Brenecki
quelle
5
Für das, was es wert ist, kann das Gleiche passieren this.props.<whatever>, was für mich das Problem war. Danke, Adam!
Don
Ja! Dies kann auch passieren, wenn Sie eine berechnete Variable, in der Sie definieren render(), oder einen Ausdruck im Tag selbst übergeben - alles, was ausgewertet wird undefined. Froh, dass ich helfen konnte!
Leigh Brenecki
1
Vielen Dank für diese Antwort: Auch wenn es nicht die akzeptierte ist, erklärt es das Problem weitaus besser als nur "a angeben name".
Machineghost
1
Ich habe meine Antwort aktualisiert, um zu erklären, wie die gesteuerte Eingabe funktioniert. Es ist erwähnenswert, dass es hier nicht darauf ankommt, dass ein Wert von undefinedzunächst übergeben wird. Es ist vielmehr die Tatsache, dass this.state.namees keine Zustandsvariable gibt, die die Eingabe unkontrolliert macht. Zum Beispiel this.state = { name: undefined };würde dies dazu führen , dass die Eingabe gesteuert wird. Es versteht sich, dass es darauf ankommt, woher der Wert kommt, nicht darauf, was der Wert ist.
Fvgs
1
@fvgs this.state = { name: undefined }hätte immer noch zu einer unkontrollierten Eingabe. <input value={this.state.name} />Desugars zu React.createElement('input', {value: this.state.name}). Da der Zugriff auf eine nicht vorhandene Eigenschaft eines Objekts zurückgegeben wird undefined, wird genau der gleiche Funktionsaufruf ausgewertet - React.createElement('input', {value: undefined})ob er namenicht festgelegt oder explizit festgelegt ist undefined, sodass sich React auf dieselbe Weise verhält. Sie können dieses Verhalten in dieser JSFiddle sehen.
Leigh Brenecki
52

Ein anderer Ansatz könnte darin bestehen, den Standardwert in Ihrer Eingabe wie folgt festzulegen:

 <input name="name" type="text" value={this.state.name || ''} onChange={this.onFieldChange('name').bind(this)}/>
JpCrow
quelle
1
<input name = "name" type = "text" defaultValue = "" onChange = {this.onFieldChange ('name'). bind (this)} /> Ich denke, das würde auch funktionieren
gandalf
Vielen Dank, genau das habe ich gesucht. Gibt es Nachteile oder mögliche Probleme bei der Verwendung dieses Musters, die ich berücksichtigen sollte?
Marcel Otten
Dies hat bei mir funktioniert, da Felder dynamisch von der API gerendert werden, sodass ich den Namen des Felds beim Mounten der Komponente nicht kenne. Das war ein Vergnügen!
Jamie - Fenrir Digital Ltd
18

Ich weiß, dass andere dies bereits beantwortet haben. Aber ein sehr wichtiger Faktor, der anderen Menschen helfen kann, die ein ähnliches Problem haben:

onChangeIn Ihrem Eingabefeld muss ein Handler hinzugefügt sein (z. B. textField, Kontrollkästchen, Radio usw.). Behandeln Sie Aktivitäten immer über den onChangeHandler.

Beispiel:

<input ... onChange={ this.myChangeHandler} ... />

Wenn Sie mit Kontrollkästchen arbeiten Sie möglicherweise dessen checkedStatus mit bearbeiten !!.

Beispiel:

<input type="checkbox" checked={!!this.state.someValue} onChange={.....} >

Referenz: https://github.com/facebook/react/issues/6779#issuecomment-326314716

Muhammad Hannan
quelle
1
das funktioniert für mich, danke, ja, ich bin zu 100% damit einverstanden, der Anfangszustand ist {}, also ist der überprüfte Wert undefiniert und macht ihn unkontrolliert.
Ping Woo
13

Eine einfache Lösung zur Behebung dieses Problems besteht darin, standardmäßig einen leeren Wert festzulegen:

<input name='myInput' value={this.state.myInput || ''} onChange={this.handleChange} />
MUSTAPHA GHLISSI
quelle
8

Ein möglicher Nachteil beim Setzen des Feldwerts auf "" (leere Zeichenfolge) im Konstruktor besteht darin, dass das Feld ein optionales Feld ist und unbearbeitet bleibt. Sofern Sie vor dem Versenden Ihres Formulars keine Massagen durchführen, bleibt das Feld als leere Zeichenfolge anstelle von NULL in Ihrem Datenspeicher erhalten.

Diese Alternative vermeidet leere Zeichenfolgen:

constructor(props) {
    super(props);
    this.state = {
        name: null
    }
}

... 

<input name="name" type="text" value={this.state.name || ''}/>
Greg R Taylor
quelle
6

Wenn Sie onChange={this.onFieldChange('name').bind(this)}in Ihrer Eingabe verwenden, müssen Sie Ihre leere Statuszeichenfolge als Wert des Eigenschaftsfelds deklarieren.

falscher Weg:

this.state ={
       fields: {},
       errors: {},
       disabled : false
    }

der richtige Weg:

this.state ={
       fields: {
         name:'',
         email: '',
         message: ''
       },
       errors: {},
       disabled : false
    }
KARTHIKEYAN.A
quelle
6

In meinem Fall fehlte mir etwas wirklich Triviales.

<input value={state.myObject.inputValue} />

Mein Zustand war der folgende, als ich die Warnung erhielt:

state = {
   myObject: undefined
}

Durch Abwechseln meines Status, um auf die Eingabe meines Werts zu verweisen , wurde mein Problem gelöst:

state = {
   myObject: {
      inputValue: ''
   }
}
Menelaos Kotsollaris
quelle
2
Vielen Dank, dass Sie mir geholfen haben, das eigentliche Problem zu verstehen, das ich hatte
rotimi-best
3

Wenn die Requisiten für Ihre Komponente als Status übergeben wurden, geben Sie einen Standardwert für Ihre Eingabe-Tags ein

<input type="text" placeholder={object.property} value={object.property ? object.property : ""}>
Kaiser Krauser
quelle
3

Setzen Sie im Ausgangszustand einen Wert auf die Eigenschaft 'name'.

this.state={ name:''};

lakmal_sathyajith
quelle
3

Ein Update dafür. Für React Hooks verwendenconst [name, setName] = useState(" ")

Jordan Mullen
quelle
Vielen Dank 4 das Update Jordan, dieser Fehler ist ein bisschen schwer zu lösen
Juan Salvador
Warum " "und nicht ""? Dies führt dazu, dass Sie jeglichen Hinweistext verlieren. Wenn Sie auf klicken und tippen, wird vor den von Ihnen eingegebenen Daten ein hinterhältiger Bereich angezeigt.
Joshua Wade
1

Dies geschieht im Allgemeinen nur, wenn Sie den Wert der Datei beim Start der Anwendung nicht steuern und nachdem ein Ereignis oder eine Funktion ausgelöst oder der Status geändert wurde, versuchen Sie nun, den Wert im Eingabefeld zu steuern.

Dieser Übergang, keine Kontrolle über die Eingabe zu haben und dann die Kontrolle darüber zu haben, führt dazu, dass das Problem überhaupt erst auftritt.

Der beste Weg, dies zu vermeiden, besteht darin, einen Wert für die Eingabe im Konstruktor der Komponente zu deklarieren. Damit das Eingabeelement vom Start der Anwendung an Wert hat.

Ashish Singh
quelle
0

Um die Statuseigenschaften für Formulareingaben dynamisch festzulegen und zu steuern, können Sie Folgendes tun:

const inputs = [
    { name: 'email', type: 'email', placeholder: "Enter your email"},
    { name: 'password', type: 'password', placeholder: "Enter your password"},
    { name: 'passwordConfirm', type: 'password', placeholder: "Confirm your password"},
]

class Form extends Component {
  constructor(props){
    super(props)
    this.state = {} // Notice no explicit state is set in the constructor
  }

  handleChange = (e) => {
    const { name, value } = e.target;

    this.setState({
      [name]: value
    }
  }

  handleSubmit = (e) => {
    // do something
  }

  render() {
     <form onSubmit={(e) => handleSubmit(e)}>
       { inputs.length ?
         inputs.map(input => {
           const { name, placeholder, type } = input;
           const value = this.state[name] || ''; // Does it exist? If so use it, if not use an empty string

           return <input key={name}  type={type} name={name} placeholder={placeholder} value={value} onChange={this.handleChange}/>
       }) :
         null
       }
       <button type="submit" onClick={(e) => e.preventDefault }>Submit</button>
     </form>    
  }
}
Kennzeichen
quelle
0

Erstellen Sie einfach einen Fallback auf '', wenn der Name this.state.name null ist.

<input name="name" type="text" value={this.state.name || ''} onChange={this.onFieldChange('name').bind(this)}/>

Dies funktioniert auch mit den useState-Variablen.

RobKohr
quelle