React: modifica di un input non controllato


360

Ho un semplice componente di reazione con il modulo che credo abbia un input controllato:

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;

Quando eseguo la mia applicazione ricevo il seguente avviso:

Avvertenza: MyForm sta modificando un input non controllato di tipo testo da controllare. Gli elementi di input non devono passare da incontrollati a controllati (o viceversa). Decidi se utilizzare un elemento di input controllato o non controllato per la durata del componente

Credo che il mio input sia controllato poiché ha un valore. Mi chiedo cosa sto facendo di sbagliato?

Sto usando React 15.1.0

Risposte:


510

Credo che il mio input sia controllato poiché ha un valore.

Perché un input sia controllato, il suo valore deve corrispondere a quello di una variabile di stato.

Tale condizione inizialmente non è soddisfatta nel tuo esempio perché this.state.nameinizialmente non è impostata. Pertanto, l'input inizialmente non è controllato. Una volta che il onChangegestore viene attivato per la prima volta,this.state.name viene impostato. A quel punto, la condizione di cui sopra è soddisfatta e l'ingresso è considerato controllato. Questa transizione da incontrollata a controllata produce l'errore visto sopra.

Inizializzando this.state.name nel costruttore:

per esempio

this.state = { name: '' };

l'ingresso sarà controllato dall'inizio, risolvendo il problema. Vedi componenti controllati React per ulteriori esempi.

Non correlato a questo errore, è necessario disporre di una sola esportazione predefinita. Il codice sopra ha due.


7
È difficile leggere la risposta e seguire l'idea molte volte, ma questa risposta è il modo perfetto per raccontare storie e far capire allo spettatore allo stesso tempo. Rispondi a livello dio!
— surajnew55,

2
Cosa succede se si hanno campi dinamici in un ciclo? ad es. hai impostato il nome del campo name={'question_groups.${questionItem.id}'}?
— user3574492

2
Come funzionerebbero i campi dinamici. Sto generando il modulo in modo dinamico e quindi impostando i nomi dei campi in uno stato interno dell'oggetto, devo ancora prima impostare manualmente i nomi dei campi nello stato e poi trasferirli sul mio oggetto?
— Giuseppe

13
Questa risposta è leggermente errata. Un input viene controllato se il valueprop ha un valore non nullo / non definito. L'elica non deve necessariamente corrispondere a una variabile di stato (potrebbe essere solo una costante e il componente sarebbe comunque considerato controllato). La risposta di Adam è più corretta e dovrebbe essere accettata.
— ecraig12345,

2
Sì, questa è tecnicamente la risposta sbagliata (ma utile). Una nota al riguardo: se hai a che fare con i pulsanti di opzione (il cui "valore" è controllato in modo leggermente diverso), questo avviso di reazione può effettivamente essere lanciato anche se il tuo componente è controllato (attualmente). github.com/facebook/react/issues/6779 e può essere corretto aggiungendo un !! per la verità di isChecked
— mheavers

123

Quando esegui il rendering del componente per la prima volta, this.state.namenon è impostato, quindi viene valutato undefinedo null, e finisci per passare value={undefined}o value={null}al tuo input.

Quando ReactDOM verifica se un campo è controllato, controlla sevalue != null (nota che non lo !=è !==) e, poiché undefined == nullin JavaScript, decide che non è controllato.

Quindi, quando onFieldChange()viene chiamato, this.state.nameè impostato su un valore stringa, l'input passa dall'essere incontrollato al controllo.

Se lo fai this.state = {name: ''}nel tuo costruttore, perché '' != null, il tuo input avrà un valore per tutto il tempo e quel messaggio sparirà.


5
Per quello che vale, può succedere la stessa cosa this.props.<whatever>, che è stato il problema da parte mia. Grazie Adam!
— Don

Sì! Questo può accadere anche se passi una variabile calcolata in cui definisci render(), o un'espressione nel tag stesso, qualsiasi cosa a cui viene valutata undefined. Sono contento di poterti aiutare!
— Leigh Brenecki il

1
Grazie per questa risposta: anche se non è accettata, spiega il problema molto meglio di "specificare un name".
— machineghost,

1
Ho aggiornato la mia risposta per spiegare come funziona l'ingresso controllato. Vale la pena notare che ciò che conta qui non è che undefinedinizialmente venga passato un valore di . Piuttosto, è il fatto che this.state.namenon esiste come variabile di stato che rende incontrollato l'input. Ad esempio, se this.state = { name: undefined };avessi comportato il controllo dell'ingresso. Dovrebbe essere chiaro che ciò che conta è da dove viene il valore, non quale sia il valore.
— fvgs

1
L'avere @fvgs this.state = { name: undefined }comporterebbe comunque un input non controllato. <input value={this.state.name} />desugars to React.createElement('input', {value: this.state.name}). Poiché l'accesso a una proprietà inesistente di un oggetto ritorna undefined, ciò restituisce esattamente la stessa React.createElement('input', {value: undefined})chiamata di funzione - se namenon è impostato o esplicitamente impostato su undefined, quindi React si comporta allo stesso modo. Puoi vedere questo comportamento in questo JSFiddle.
— Leigh Brenecki,

52

Un altro approccio potrebbe essere l'impostazione del valore predefinito all'interno dell'input, in questo modo:

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

1
<input name = "name" type = "text" defaultValue = "" onChange = {this.onFieldChange ('name'). bind (this)} /> Penso che funzionerebbe anche
— gandalf

Grazie mille, era esattamente quello che stavo cercando. Ci sono svantaggi o potenziali problemi nell'utilizzare questo schema che dovrei tenere a mente?
— Marcel Otten,

Questo ha funzionato per me, poiché i campi sono dinamicamente resi dall'API, quindi non conosco il nome del campo quando il componente è montato. Questo ha funzionato a meraviglia!
— Jamie - Fenrir Digital Ltd,

18

So che altri hanno già risposto. Ma un fattore molto importante qui che può aiutare altre persone con problemi simili:

Devi avere un onChangegestore aggiunto nel tuo campo di input (es. TextField, checkbox, radio, ecc.). Gestire sempre l'attività tramite il onChangegestore.

Esempio:

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

Quando si lavora con la casella di controllo , potrebbe essere necessario gestirne lo checkedstato con !!.

Esempio:

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

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


1
questo funziona per me, grazie, sì, sono d'accordo al 100% con quello, lo stato iniziale è {}, quindi il valore verificato sarà indefinito e lo renderà incontrollato,
— Ping Woo,

13

La soluzione semplice per risolvere questo problema consiste nell'impostare un valore vuoto per impostazione predefinita:

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

8

Un potenziale svantaggio con l'impostazione del valore del campo su "" (stringa vuota) nel costruttore è se il campo è un campo opzionale e non viene modificato. A meno che non si esegua un massaggio prima di pubblicare il modulo, il campo verrà mantenuto nell'archivio dati come una stringa vuota anziché NULL.

Questa alternativa eviterà le stringhe vuote:

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

... 

<input name="name" type="text" value={this.state.name || ''}/>

6

Quando si utilizza onChange={this.onFieldChange('name').bind(this)}l'input, è necessario dichiarare la stringa vuota del proprio stato come valore del campo proprietà.

modo errato:

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

modo corretto:

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

6

Nel mio caso, mi mancava qualcosa di veramente banale.

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

Il mio stato era il seguente quando stavo ricevendo l'avvertimento:

state = {
   myObject: undefined
}

Alternando il mio stato per fare riferimento all'input del mio valore , il mio problema è stato risolto:

state = {
   myObject: {
      inputValue: ''
   }
}

2
Grazie per avermi aiutato a capire il vero problema che stavo avendo
— rotimi-best

3

Se gli oggetti di scena sul componente sono stati passati come stato, inserisci un valore predefinito per i tag di input

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


3

Un aggiornamento per questo. Utilizzare per React Hooksconst [name, setName] = useState(" ")


Grazie 4 all'aggiornamento Giordania, questo errore è un po 'difficile da risolvere
— Juan Salvador,

Perché " "e no ""? Questo ti fa perdere qualsiasi testo di suggerimento e se fai clic e digiti ottieni uno spazio subdolo prima di qualsiasi dato inserito.
— Joshua Wade,

1

Ciò si verifica generalmente solo quando non si controlla il valore del file all'avvio dell'applicazione e dopo un evento o una funzione attivata o lo stato modificato, si sta ora cercando di controllare il valore nel campo di input.

Questa transizione di non avere il controllo sull'input e quindi avere il controllo su di esso è ciò che causa il problema in primo luogo.

Il modo migliore per evitarlo è dichiarare un valore per l'input nel costruttore del componente. In modo che l'elemento di input abbia valore dall'inizio dell'applicazione.


0

Per impostare dinamicamente le proprietà dello stato per gli input dei moduli e mantenerli controllati, si potrebbe fare qualcosa del genere:

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>    
  }
}

0

È sufficiente creare un fallback su "" se this.state.name è null.

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

Questo funziona anche con le variabili useState.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.