Um componente está alterando uma entrada não controlada do tipo de texto para ser erro controlado no ReactJS

331

Aviso: Um componente está alterando uma entrada não controlada do tipo de texto a ser controlado. Os elementos de entrada não devem mudar de não controlado para controlado (ou vice-versa). Decida entre usar um elemento de entrada controlado ou não controlado durante a vida útil do componente. *

A seguir está o meu código:

constructor(props) {
  super(props);
  this.state = {
    fields: {},
    errors: {}
  }
  this.onSubmit = this.onSubmit.bind(this);
}

....

onChange(field, e){
  let fields = this.state.fields;
  fields[field] = e.target.value;
  this.setState({fields});
}

....

render() {
  return(
    <div className="form-group">
      <input
        value={this.state.fields["name"]}
        onChange={this.onChange.bind(this, "name")}
        className="form-control"
        type="text"
        refs="name"
        placeholder="Name *"
      />
      <span style={{color: "red"}}>{this.state.errors["name"]}</span>
    </div>
  )
}
Riya Kapuria
fonte
2
qual é o valor inicial de fieldsno estado?
Mayank Shukla
2
construtor (adereços) {super (adereços); this.state = {fields: {}, erros: {}} this.onSubmit = this.onSubmit.bind (this); }
Riya Kapuria 30/10
2
Possível duplicado de Reagir - mudando uma entrada descontrolada
Michael Freidgeim

Respostas:

651

O motivo é que, no estado que você definiu:

this.state = { fields: {} }

campos como um objeto em branco, por isso durante a primeira prestação this.state.fields.nameserá undefined, eo campo de entrada terá o seu valor como:

value={undefined}

Por esse motivo, o campo de entrada se tornará descontrolado.

Depois de inserir qualquer valor na entrada, o fieldsestado in é alterado para:

this.state = { fields: {name: 'xyz'} }

E nesse momento o campo de entrada é convertido em um componente controlado; é por isso que você está recebendo o erro:

Um componente está alterando uma entrada não controlada do tipo de texto a ser controlado.

Soluções possíveis:

1- Defina o fieldsestado in como:

this.state = { fields: {name: ''} }

2- Ou defina a propriedade value usando a avaliação de curto-circuito da seguinte maneira:

value={this.state.fields.name || ''}   // (undefined || '') = ''
Mayank Shukla
fonte
5
existe um motivo indefinido para "não controlado" enquanto o último é "controlado" - o que isso significa?
Prashanth Subramanian
2
porque ''é um valor de sequência válido. portanto, quando você muda ''para 'xyz', o tipo de entrada não muda os meios controlados para controlados. Mas no caso undefinedda xyztipo mudará de descontrolada controlada.
Mayank Shukla
1
Obrigado, isso funcionou para mim:value={this.state.fields.name || ''}
Bob
1
Eu não sabia que, uma vez que o valor se torna indefinido, a entrada se torna automaticamente descontrolada, ótimo. Já solucione meu problema
Adrian Serna
1
surpreendente! porque não vi isso mencionado nos documentos oficiais ou sou cego? link para a fonte, por favor :)
Dheeraj
34

Alterar valuepara defaultValueresolverá isso.

Nota :

defaultValueé apenas para o carregamento inicial. Se você deseja inicializar o arquivo input, deve usar defaultValue, mas se deseja statealterar o valor, é necessário usá-lo value. Leia isto para mais informações.

Eu usei value={this.state.input ||""}em inputse livrar desse aviso.

MoFoLuWaSo
fonte
2
Obrigado! Me deparei com esse problema e as outras soluções não se aplicavam a mim, mas essa solução ajudou.
PepperAddict
14

Dentro do componente, coloque a caixa de entrada da seguinte maneira.

<input className="class-name"
              type= "text"
              id="id-123"
              value={ this.state.value || "" }
              name="field-name"
              placeholder="Enter Name"
              />
Tabish
fonte
13

SIMPLESMENTE, você deve definir o estado inicial primeiro

Se você não definir o estado inicial, o reagir tratará isso como um componente não controlado

Vaibhav Bacchav
fonte
10

Além da resposta aceita, se você estiver usando um inputtipo checkboxou radio, descobri que preciso verificar nulo / indefinido o checkedatributo também.

<input
  id={myId}
  name={myName}
  type="checkbox" // or "radio"
  value={myStateValue || ''}
  checked={someBoolean ? someBoolean : false}
  />

E se você estiver usando TS (ou Babel), poderá usar coalescimento nulo em vez do operador OR lógico:

value={myStateValue ?? ''}
checked={someBoolean ?? false}
Lynden Noye
fonte
8

Definir o estado atual primeiro ...this.state

É porque quando você vai atribuir um novo estado, ele pode estar indefinido. por isso será corrigido definindo o estado que extrai o estado atual também

this.setState({...this.state, field})

Se houver um objeto em seu estado, você deve definir o estado da seguinte forma, suponha que você precise definir o nome de usuário dentro do objeto de usuário.

this.setState({user:{...this.state.user, ['username']: username}})
NuOne
fonte
1
Pelo que entendi, setState já substitui apenas os campos, portanto, no primeiro caso, seria desnecessário desestruturar a atribuição. No segundo estado, pode ser necessário, pois o setState substituirá um atacado de subobjeto.
26419 Kzqai
6
const [name, setName] = useState()

gera erro assim que você digita no campo de texto

const [name, setName] = useState('') // <-- by putting in quotes 

irá corrigir o problema neste exemplo de string.

Nelles
fonte
2

Se você usar várias entradas no campo, siga: Por exemplo:

class AddUser extends React.Component {
   constructor(props){
     super(props);

     this.state = {
       fields: { UserName: '', Password: '' }
     };
   }

   onChangeField = event => {
    let name = event.target.name;
    let value = event.target.value;
    this.setState(prevState => {
        prevState.fields[name] =  value;
        return {
           fields: prevState.fields
        };
    });
  };

  render() { 
     const { UserName, Password } = this.state.fields;
     return (
         <form>
             <div>
                 <label htmlFor="UserName">UserName</label>
                 <input type="text" 
                        id='UserName' 
                        name='UserName'
                        value={UserName}
                        onChange={this.onChangeField}/>
              </div>
              <div>
                  <label htmlFor="Password">Password</label>
                  <input type="password" 
                         id='Password' 
                         name='Password'
                         value={Password}
                         onChange={this.onChangeField}/>
              </div>
         </form>
     ); 
  }
}

Pesquise seu problema em:

onChangeField = event => {
    let name = event.target.name;
    let value = event.target.value;
    this.setState(prevState => {
        prevState.fields[name] =  value;
        return {
            fields: prevState.fields
        };
    });
};
Hamidreza Rafiei
fonte
1

O uso de ganchos de reação também não se esqueça de definir o valor inicial.
Eu estava usando <input type='datetime-local' value={eventStart} />e inicial eventStartera como

const [eventStart, setEventStart] = useState();
em vez disso
const [eventStart, setEventStart] = useState('');.

A cadeia vazia entre parênteses é a diferença.
Além disso, se você redefinir o formulário após o envio como eu, novamente, será necessário configurá-lo como sequência vazia, não apenas entre parênteses vazios.

Esta é apenas minha pequena contribuição para esse tópico, talvez isso ajude alguém.

Zrna
fonte
0

No meu caso, foi praticamente o que a principal resposta de Mayank Shukla diz. O único detalhe era que meu estado carecia completamente da propriedade que eu estava definindo.

Por exemplo, se você tiver este estado:

state = {
    "a" : "A",
    "b" : "B",
}

Se você estiver expandindo seu código, poderá adicionar um novo suporte. Em outro lugar, você poderá criar uma nova propriedade ccujo valor não seja apenas indefinido no estado do componente, mas na própria propriedade seja definida.

Para resolver isso, certifique-se de adicionar c ao seu estado e fornecer um valor inicial adequado.

por exemplo,

state = {
    "a" : "A",
    "b" : "B",
    "c" : "C", // added and initialized property!
}

Espero ter sido capaz de explicar meu caso extremo.

Daniel Segura
fonte
0

Estou recebendo o mesmo aviso: Um componente está alterando uma entrada não controlada do tipo oculta para ser controlada. Não consigo resolver o meu problema. Alguma idéia do porquê?

export default ({  valueName, onChangeAllg,}) => {

          <TextField
            id={"name"}
            select
            label={"name"}
            value={valueName.geschlecht || ""}
            name={"name"}
            onChange={onChangeAllg}

          >
            {nameList.map((option) => (
              <MenuItem key={option.value} value={option.value}>
                {option.label}
              </MenuItem>
            ))}
          </TextField>

eu tentei ({ valueName, valueName: {geschlecht=""}, onChangeAllg,})evalue={valueName.geschlecht || ""}

Dr. Malte Westerloh
fonte
Eu encontrei. defaultValue={""}Faltava um interior do TextField
Dr. Malte Westerloh
0

Aviso: Um componente está alterando uma entrada não controlada do tipo de texto a ser controlado. Os elementos de entrada não devem mudar de não controlado para controlado (ou vice-versa). Decida entre usar um elemento de entrada controlado ou não controlado durante a vida útil do componente.

Solução: verifique se o valor não está indefinido

React / Formik / Bootstrap / TypeScript

exemplo:

{ values?.purchaseObligation.remainingYear ?
  <Input
   tag={Field}
   name="purchaseObligation.remainingYear"
   type="text"
   component="input"
  /> : null
}
user3706761
fonte