Bereaksi - mengubah input yang tidak terkontrol


360

Saya memiliki komponen reaksi sederhana dengan bentuk yang saya yakini memiliki satu input terkontrol:

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;

Ketika saya menjalankan aplikasi saya, saya mendapat peringatan berikut:

Peringatan: MyForm sedang mengubah input teks tipe yang tidak terkontrol untuk dikontrol. Elemen input tidak boleh beralih dari tidak terkontrol ke terkontrol (atau sebaliknya). Putuskan antara menggunakan elemen input yang dikontrol atau tidak dikontrol selama masa pakai komponen

Saya percaya input saya dikontrol karena memiliki nilai. Saya bertanya-tanya apa yang saya lakukan salah?

Saya menggunakan Bereaksi 15.1.0

Jawaban:


510

Saya percaya input saya dikontrol karena memiliki nilai.

Agar input dikontrol, nilainya harus sesuai dengan variabel keadaan.

Kondisi itu awalnya tidak terpenuhi dalam contoh Anda karena this.state.nameawalnya tidak diatur. Oleh karena itu, input awalnya tidak terkendali. Setelah onChangepawang dipicu untuk pertama kalinya, this.state.namesiap. Pada saat itu, kondisi di atas terpenuhi dan input dianggap terkendali. Transisi ini dari tidak terkontrol ke terkontrol menghasilkan kesalahan yang terlihat di atas.

Dengan menginisialisasi this.state.namedalam konstruktor:

misalnya

this.state = { name: '' };

input akan dikontrol dari awal, memperbaiki masalah. Lihat Bereaksi Komponen Terkendali untuk lebih banyak contoh.

Tidak terkait dengan kesalahan ini, Anda seharusnya hanya memiliki satu ekspor default. Kode Anda di atas memiliki dua.


7
Sulit untuk membaca jawaban dan mengikuti ide berkali-kali tetapi jawaban ini adalah cara yang sempurna untuk bercerita dan membuat pemirsa mengerti pada saat yang sama. Jawab tuhan level!
surajnew55

2
Bagaimana jika Anda memiliki bidang dinamis dalam satu lingkaran? misalnya Anda menetapkan nama bidang menjadi name={'question_groups.${questionItem.id}'}?
user3574492

2
Bagaimana bidang dinamis bekerja. Apakah menghasilkan formulir secara dinamis dan kemudian menetapkan nama bidang dalam suatu objek di dalam negara bagian, apakah saya masih harus terlebih dahulu secara manual mengatur nama bidang dalam keadaan terlebih dahulu dan kemudian mentransfernya ke objek saya?
Joseph

13
Jawaban ini sedikit salah. Input dikontrol jika valueprop memiliki nilai non-null / undefined. Prop tidak perlu berhubungan dengan variabel keadaan (bisa saja berupa konstanta dan komponennya akan tetap dianggap terkontrol). Jawaban Adam lebih benar dan harus diterima.
ecraig12345

2
Ya - ini secara teknis jawaban yang salah (tetapi membantu). Catatan tentang ini - jika Anda berurusan dengan tombol radio (yang 'nilainya' dikontrol sedikit berbeda), peringatan reaksi ini benar-benar dapat melempar bahkan jika komponen Anda dikontrol (saat ini). github.com/facebook/react/issues/6779 , dan dapat diperbaiki dengan menambahkan !! untuk kebenaran isChecked
mheavers

123

Ketika Anda pertama kali merender komponen Anda, this.state.nametidak disetel, jadi ia mengevaluasi ke undefinedatau null, dan Anda akhirnya melewati value={undefined}atau value={null}ke Anda input.

Ketika ReactDOM memeriksa untuk melihat apakah suatu bidang dikontrol, ia memeriksa untuk melihat apakahvalue != null (perhatikan bahwa itu !=bukan !==), dan karena undefined == nulldalam JavaScript, ia memutuskan bahwa bidang itu tidak terkontrol.

Jadi, ketika onFieldChange()dipanggil, this.state.namediatur ke nilai string, input Anda berubah dari tidak terkontrol menjadi terkontrol.

Jika Anda melakukannya this.state = {name: ''}di konstruktor Anda, karena '' != null, input Anda akan memiliki nilai sepanjang waktu, dan pesan itu akan hilang.


5
Untuk apa nilainya, hal yang sama dapat terjadi this.props.<whatever>, yang merupakan masalah pada saya. Terima kasih, Adam!
Don

Ya! Ini juga bisa terjadi jika Anda melewatkan variabel terhitung yang Anda tetapkan render(), atau ekspresi dalam tag itu sendiri — apa pun yang dievaluasi undefined. Senang bisa membantu!
Leigh Brenecki

1
Terima kasih atas jawaban ini: meskipun bukan yang diterima itu menjelaskan masalah ini jauh lebih baik daripada hanya "menentukan name".
machineghost

1
Saya telah memperbarui jawaban saya untuk menjelaskan cara kerja input terkontrol. Perlu dicatat bahwa yang penting di sini bukanlah nilai undefinedyang pada awalnya disahkan. Sebaliknya, itu fakta yang this.state.nametidak ada sebagai variabel keadaan yang membuat input tidak terkontrol. Misalnya, memiliki this.state = { name: undefined };akan mengakibatkan input dikontrol. Harus dipahami bahwa yang penting adalah dari mana nilai itu berasal, bukan apa nilainya.
fvgs

1
@fvgs this.state = { name: undefined }masih akan menghasilkan input yang tidak terkendali. <input value={this.state.name} />keinginan untuk React.createElement('input', {value: this.state.name}). Karena mengakses properti objek yang tidak ada kembali undefined, ini mengevaluasi ke panggilan fungsi yang sama persis—— React.createElement('input', {value: undefined})apakah nametidak diatur atau secara eksplisit diatur ke undefined, jadi Bereaksi berperilaku dengan cara yang sama. Anda bisa melihat perilaku ini di JSFiddle ini.
Leigh Brenecki

52

Pendekatan lain bisa dengan menetapkan nilai default di dalam input Anda, seperti ini:

 <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)} /> Saya pikir ini juga akan berfungsi
gandalf

Terima kasih banyak, ini sebenarnya yang saya cari. Apakah ada kelemahan atau potensi masalah menggunakan pola ini yang harus saya ingat?
Marcel Otten

Ini bekerja untuk saya, karena bidang secara dinamis diberikan dari API jadi saya tidak tahu nama bidang ketika komponen dipasang. Ini berhasil!
Jamie - Fenrir Digital Ltd

18

Saya tahu orang lain sudah menjawab ini. Tetapi faktor yang sangat penting di sini yang dapat membantu orang lain mengalami masalah serupa:

Anda harus memiliki onChangepenangan yang ditambahkan di bidang input Anda (mis. TextField, kotak centang, radio, dll). Selalu tangani aktivitas melalui onChangepawang.

Contoh:

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

Saat Anda bekerja dengan kotak centang, Anda mungkin perlu menangani checkedkondisinya !!.

Contoh:

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

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


1
ini bekerja untuk saya, terima kasih, ya, saya 100% setuju dengan itu, keadaan awal adalah {}, jadi nilai yang dicentang akan tidak terdefinisi dan membuatnya tidak terkendali,
Ping Woo

13

Solusi sederhana untuk mengatasi masalah ini adalah dengan menetapkan nilai kosong secara default:

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

8

Satu potensi downside dengan menyetel nilai bidang ke "" (string kosong) di konstruktor adalah jika bidang adalah bidang opsional dan dibiarkan tanpa diedit. Kecuali jika Anda melakukan pemijatan sebelum memposting formulir Anda, bidang tersebut akan tetap disimpan ke data Anda sebagai string kosong alih-alih NULL.

Alternatif ini akan menghindari string kosong:

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

... 

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

6

Ketika Anda menggunakan onChange={this.onFieldChange('name').bind(this)}input Anda, Anda harus mendeklarasikan string kosong status Anda sebagai nilai bidang properti.

cara yang salah:

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

jalan yang benar:

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

6

Dalam kasus saya, saya kehilangan sesuatu yang sangat sepele.

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

Keadaan saya adalah yang berikut ketika saya mendapat peringatan:

state = {
   myObject: undefined
}

Dengan mengganti status saya untuk referensi input nilai saya, masalah saya terpecahkan:

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

2
Terima kasih telah membantu saya memahami masalah sebenarnya yang saya alami
terbaik

3

Jika alat peraga pada komponen Anda diteruskan sebagai keadaan, beri nilai default untuk tag input Anda

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


3

Pembaruan untuk ini. Untuk React Hooks gunakanconst [name, setName] = useState(" ")


Terima kasih 4 pembaruan Jordan, kesalahan ini agak sulit untuk dipecahkan
Juan Salvador

Kenapa " "dan tidak ""? Ini menyebabkan Anda kehilangan teks petunjuk, dan jika Anda mengklik dan mengetik Anda mendapat ruang licik sebelum data apa pun yang Anda masukkan.
Joshua Wade

1

Ini umumnya terjadi hanya ketika Anda tidak mengendalikan nilai yang diajukan ketika aplikasi dimulai dan setelah beberapa peristiwa atau beberapa fungsi dipecat atau negara berubah, Anda sekarang mencoba untuk mengontrol nilai di bidang input.

Transisi tidak memiliki kendali atas input dan kemudian memiliki kendali atas hal inilah yang menyebabkan masalah terjadi.

Cara terbaik untuk menghindari ini adalah dengan mendeklarasikan beberapa nilai untuk input dalam konstruktor komponen. Sehingga elemen input memiliki nilai dari awal aplikasi.


0

Untuk menyetel properti keadaan secara dinamis untuk input formulir dan menjaganya tetap terkendali, Anda bisa melakukan sesuatu seperti ini:

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

Cukup buat fallback ke '' jika this.state.name adalah nol.

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

Ini juga berfungsi dengan variabel useState.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.