React input defaultValue tidak diperbarui dengan status

94

Saya mencoba membuat formulir sederhana dengan react, tetapi menghadapi kesulitan karena data terikat dengan benar ke defaultValue formulir.

Perilaku yang saya cari adalah ini:

  1. Saat saya membuka halaman saya, bidang input Teks harus diisi dengan teks AwayMessage saya di database saya. Itu adalah "Contoh Teks"
  2. Idealnya saya ingin memiliki tempat penampung di bidang input Teks jika AwayMessage di database saya tidak memiliki teks.

Namun, saat ini, saya menemukan bahwa bidang input Teks kosong setiap kali saya menyegarkan halaman. (Meskipun apa yang saya ketikkan ke dalam input memang menyimpan dengan benar dan bertahan.) Saya pikir ini karena html kolom teks input dimuat ketika AwayMessage adalah objek kosong, tetapi tidak menyegarkan ketika awayMessage dimuat. Selain itu, saya tidak dapat menentukan nilai default untuk bidang tersebut.

Saya menghapus beberapa kode untuk kejelasan (yaitu onToggleChange)

    window.Pages ||= {}

    Pages.AwayMessages = React.createClass

      getInitialState: ->
        App.API.fetchAwayMessage (data) =>
        @setState awayMessage:data.away_message
        {awayMessage: {}}

      onTextChange: (event) ->
        console.log "VALUE", event.target.value

      onSubmit: (e) ->
        window.a = @
        e.preventDefault()
        awayMessage = {}
        awayMessage["master_toggle"]=@refs["master_toggle"].getDOMNode().checked
        console.log "value of text", @refs["text"].getDOMNode().value
        awayMessage["text"]=@refs["text"].getDOMNode().value
        @awayMessage(awayMessage)

      awayMessage: (awayMessage)->
        console.log "I'm saving", awayMessage
        App.API.saveAwayMessage awayMessage, (data) =>
          if data.status == 'ok'
            App.modal.closeModal()
            notificationActions.notify("Away Message saved.")
            @setState awayMessage:awayMessage

      render: ->
        console.log "AWAY_MESSAGE", this.state.awayMessage
        awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else "Placeholder Text"
        `<div className="away-messages">
           <div className="header">
             <h4>Away Messages</h4>
           </div>
           <div className="content">
             <div className="input-group">
               <label for="master_toggle">On?</label>
               <input ref="master_toggle" type="checkbox" onChange={this.onToggleChange} defaultChecked={this.state.awayMessage.master_toggle} />
             </div>
             <div className="input-group">
               <label for="text">Text</label>
               <input ref="text" onChange={this.onTextChange} defaultValue={awayMessageText} />
             </div>
           </div>
           <div className="footer">
             <button className="button2" onClick={this.close}>Close</button>
             <button className="button1" onClick={this.onSubmit}>Save</button>
           </div>
         </div>

console.log saya untuk AwayMessage menampilkan yang berikut:

AWAY_MESSAGE Object {}
AWAY_MESSAGE Object {id: 1, company_id: 1, text: "Sample Text", master_toggle: false}
Neeharika Bhartiya
sumber

Jawaban:

61

defaultValue hanya untuk pemuatan awal

Jika Anda ingin menginisialisasi input maka Anda harus menggunakan defaultValue, tetapi jika Anda ingin menggunakan state untuk mengubah nilainya maka Anda perlu menggunakan value. Secara pribadi saya suka menggunakan defaultValue jika saya hanya menginisialisasi dan kemudian hanya menggunakan refs untuk mendapatkan nilai saat saya mengirimkan. Ada info lebih lanjut tentang referensi dan masukan di dokumen react, https://facebook.github.io/react/docs/forms.html dan https://facebook.github.io/react/docs/working-with-the- browser.html .

Inilah cara saya menulis ulang masukan Anda:

awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else ''
<input ref="text" onChange={this.onTextChange} placeholder="Placeholder Text" value={@state.awayMessageText} />

Anda juga tidak ingin meneruskan teks placeholder seperti yang Anda lakukan karena itu akan benar-benar menyetel nilainya menjadi 'teks placeholder'. Anda masih perlu memasukkan nilai kosong ke input karena undefined dan nil mengubah nilai menjadi defaultValue pada dasarnya. https://facebook.github.io/react/tips/controlled-input-null-value.html .

getInitialState tidak dapat melakukan panggilan api

Anda perlu melakukan panggilan api setelah getInitialState dijalankan. Untuk kasus Anda, saya akan melakukannya di componentDidMount. Ikuti contoh ini, https://facebook.github.io/react/tips/initial-ajax.html .

Saya juga merekomendasikan membaca siklus hidup komponen dengan react. https://facebook.github.io/react/docs/component-specs.html .

Tulis ulang dengan modifikasi dan status pemuatan

Secara pribadi saya tidak suka melakukan keseluruhan jika lain maka logika dalam render dan lebih suka menggunakan 'memuat' di negara bagian saya dan membuat pemintal font yang mengagumkan sebelum formulir dimuat, http://fortawesome.github.io/Font- Luar biasa / contoh / . Berikut tulisan ulang untuk menunjukkan apa yang saya maksud. Jika saya mengacaukan centang untuk cjsx, itu karena saya biasanya hanya menggunakan coffeescript seperti ini,.

window.Pages ||= {}

Pages.AwayMessages = React.createClass

  getInitialState: ->
    { loading: true, awayMessage: {} }

  componentDidMount: ->
    App.API.fetchAwayMessage (data) =>
      @setState awayMessage:data.away_message, loading: false

  onToggleCheckbox: (event)->
    @state.awayMessage.master_toggle = event.target.checked
    @setState(awayMessage: @state.awayMessage)

  onTextChange: (event) ->
    @state.awayMessage.text = event.target.value
    @setState(awayMessage: @state.awayMessage)

  onSubmit: (e) ->
    # Not sure what this is for. I'd be careful using globals like this
    window.a = @
    @submitAwayMessage(@state.awayMessage)

  submitAwayMessage: (awayMessage)->
    console.log "I'm saving", awayMessage
    App.API.saveAwayMessage awayMessage, (data) =>
      if data.status == 'ok'
        App.modal.closeModal()
        notificationActions.notify("Away Message saved.")
        @setState awayMessage:awayMessage

  render: ->
    if this.state.loading
      `<i className="fa fa-spinner fa-spin"></i>`
    else
    `<div className="away-messages">
       <div className="header">
         <h4>Away Messages</h4>
       </div>
       <div className="content">
         <div className="input-group">
           <label for="master_toggle">On?</label>
           <input type="checkbox" onChange={this.onToggleCheckbox} checked={this.state.awayMessage.master_toggle} />
         </div>
         <div className="input-group">
           <label for="text">Text</label>
           <input ref="text" onChange={this.onTextChange} value={this.state.awayMessage.text} />
         </div>
       </div>
       <div className="footer">
         <button className="button2" onClick={this.close}>Close</button>
         <button className="button1" onClick={this.onSubmit}>Save</button>
       </div>
     </div>

Itu harus menutupinya. Nah, itu adalah salah satu cara untuk membahas bentuk-bentuk yang menggunakan status dan nilai. Anda juga bisa menggunakan defaultValue sebagai ganti nilai lalu gunakan ref untuk mendapatkan nilai saat Anda mengirimkan. Jika Anda mengikuti rute itu, saya akan merekomendasikan Anda memiliki komponen kulit luar (biasanya disebut sebagai komponen orde tinggi) untuk mengambil data dan kemudian meneruskannya ke formulir sebagai alat peraga.

Secara keseluruhan saya akan merekomendasikan membaca dokumen react sepanjang jalan dan melakukan beberapa tutorial. Ada banyak blog di luar sana dan http://www.egghead.io memiliki beberapa tutorial bagus. Saya memiliki beberapa barang di situs saya juga, http://www.openmindedinnovations.com .

Blaine Hatab
sumber
Hanya ingin tahu mengapa tidak ada gunanya melakukan panggilan api dalam keadaan awal? getInitialState dijalankan tepat sebelum componentDidMount, dan panggilan API adalah async. Apakah itu lebih konvensional atau ada alasan lain di baliknya?
Mïchael Makaröv
1
Saya tidak tahu persis di mana saya membacanya tetapi saya tahu Anda tidak menempatkan panggilan api di sana. Ada perpustakaan yang dibuat untuk menghadapinya, github.com/andreypopp/react-async . Tetapi saya tidak akan menggunakan pustaka itu dan hanya akan menempatkannya di componentDidMount. Saya tahu bahwa dalam tutorial tentang dokumentasi reacts, melakukan panggilan api di componentDidMount juga.
Blaine Hatab
@ MïchaelMakaröv --karena panggilan api bersifat asinkron, dan getInitialState mengembalikan status secara sinkron. Jadi, status awal Anda akan disiapkan sebelum panggilan api selesai.
Drogon
2
Apakah aman untuk mengganti defaultValue dengan nilai? Saya tahu defaultValue hanya memuat pada inisialisasi tetapi nilai tampaknya juga melakukan ini.
stealthysnacks
2
@stealthysnacks boleh saja menggunakan nilai tetapi sekarang Anda harus menyetel nilai itu agar masukan dapat bekerja. defaultValue hanya menyetel nilai awal dan input akan dapat diubah tetapi saat menggunakan nilai, nilai itu sekarang 'dikontrol'
Blaine Hatab
60

Cara lain untuk memperbaikinya adalah dengan mengubah keyinput.

<input ref="text" key={this.state.awayMessage ? 'notLoadedYet' : 'loaded'} onChange={this.onTextChange} defaultValue={awayMessageText} />

Pembaruan: Karena ini mendapatkan suara positif, saya harus mengatakan bahwa Anda harus memiliki disabledatau readonlyprop dengan benar saat konten sedang dimuat, jadi Anda tidak mengurangi pengalaman ux.

Dan ya, kemungkinan besar ini adalah peretasan, tetapi berhasil menyelesaikannya .. ;-)

TryingToImprove
sumber
Implementasi naif - fokus bidang input berubah saat nilai kunci diubah (misalnya, keluar onKeyUp)
Arthur Kushman
2
Ya, ada beberapa kekurangan, tetapi menyelesaikan pekerjaan dengan mudah.
TryingToImprove
Ini pintar. Aku digunakan untuk selectdengan keyseperti defaultValueyang sebenarnya value.
Avi
mengubah keyinput adalah kunci untuk mendapatkan nilai baru yang tercermin dalam input. Saya menggunakannya dengan type="text"sukses.
Jacob Nelson
3

Mungkin bukan solusi terbaik, tetapi saya akan membuat komponen seperti di bawah ini sehingga saya dapat menggunakannya kembali di mana saja dalam kode saya. Saya berharap itu sudah bereaksi secara default.

<MagicInput type="text" binding={[this, 'awayMessage.text']} />

Komponennya mungkin terlihat seperti:

window.MagicInput = React.createClass

  onChange: (e) ->
    state = @props.binding[0].state
    changeByArray state, @path(), e.target.value
    @props.binding[0].setState state

  path: ->
    @props.binding[1].split('.')
  getValue: ->
    value = @props.binding[0].state
    path = @path()
    i = 0
    while i < path.length
      value = value[path[i]]
      i++
    value

  render: ->
    type = if @props.type then @props.type else 'input'
    parent_state = @props.binding[0]
    `<input
      type={type}
      onChange={this.onChange}
      value={this.getValue()}
    />`

Di mana perubahan dengan array adalah fungsi yang mengakses hash dengan jalur yang diekspresikan oleh array

changeByArray = (hash, array, newValue, idx) ->
  idx = if _.isUndefined(idx) then 0 else idx
  if idx == array.length - 1
    hash[array[idx]] = newValue
  else
    changeByArray hash[array[idx]], array, newValue, ++idx 
Mïchael Makaröv
sumber
0

Cara paling andal untuk menyetel nilai awal adalah dengan menggunakan componentDidMount () {} selain render () {}:

... 
componentDidMount(){

    const {nameFirst, nameSecond, checkedStatus} = this.props;

    document.querySelector('.nameFirst').value          = nameFirst;
    document.querySelector('.nameSecond').value         = nameSecond;
    document.querySelector('.checkedStatus').checked    = checkedStatus;        
    return; 
}
...

Anda mungkin merasa mudah untuk menghancurkan elemen dan menggantinya dengan yang baru dengan

<input defaultValue={this.props.name}/>

seperti ini:

if(document.querySelector("#myParentElement")){
    ReactDOM.unmountComponentAtNode(document.querySelector("#myParentElement"));
    ReactDOM.render(
        <MyComponent name={name}  />,
        document.querySelector("#myParentElement")
    );
};

Anda juga dapat menggunakan versi metode unmount ini:

ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode);
Roma
sumber
5
Anda memanipulasi DOM sendiri di sini .. bukankah itu TIDAK ADA yang besar sebagai reaksi?
Hancur
0

Beri nilai pada parameter "placeHolder". Sebagai contoh :-

 <input 
    type="text"
    placeHolder="Search product name."
    style={{border:'1px solid #c5c5c5', padding:font*0.005,cursor:'text'}}
    value={this.state.productSearchText}
    onChange={this.handleChangeProductSearchText}
    />
Manas Gond
sumber