Tepki girdisi defaultValue durumla güncellenmiyor


100

React ile basit bir form oluşturmaya çalışıyorum, ancak verilerin formun defaultValue değerine düzgün şekilde bağlanması konusunda zorluk yaşıyorum.

Aradığım davranış şudur:

  1. Sayfamı açtığımda, Metin giriş alanı veritabanımdaki AwayMessage metnimle doldurulmalıdır. Yani "Örnek Metin"
  2. İdeal olarak, veritabanımdaki AwayMessage'da metin yoksa, Metin giriş alanında bir yer tutucunun olmasını istiyorum.

Ancak şu anda, sayfayı her yenilediğimde Metin giriş alanının boş olduğunu görüyorum. (Girdiye yazdığım şey düzgün bir şekilde kaydediliyor ve kalıcı oluyor.) Bunun nedeni, AwayMessage boş bir nesne olduğunda girdi metin alanının html'sinin yüklenmesidir, ancak awayMessage yüklendiğinde yenilenmemesidir. Ayrıca, alan için varsayılan bir değer belirleyemiyorum.

Anlaşılır olması için kodun bir kısmını kaldırdım (yani 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>

AwayMessage için console.log'um şunları gösteriyor:

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

Yanıtlar:


65

defaultValue yalnızca ilk yükleme içindir

Girdiyi başlatmak istiyorsanız, defaultValue kullanmanız gerekir, ancak değeri değiştirmek için state'i kullanmak istiyorsanız, o zaman değeri kullanmanız gerekir. Şahsen ben sadece onu başlatıyorsam defaultValue kullanmayı ve gönderirken değeri almak için refs kullanmayı seviyorum. React belgelerinde referanslar ve girdiler hakkında daha fazla bilgi var, https://facebook.github.io/react/docs/forms.html ve https://facebook.github.io/react/docs/working-with-the- browser.html .

Girişinizi şu şekilde yeniden yazacağım:

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

Ayrıca, yaptığınız gibi yer tutucu metni geçirmek istemezsiniz çünkü bu, değeri aslında 'yer tutucu metin' olarak ayarlayacaktır. Girdiye yine de boş bir değer geçirmeniz gerekir çünkü undefined ve nil, değeri esasen defaultValue'ya çevirir. https://facebook.github.io/react/tips/controlled-input-null-value.html .

getInitialState api çağrıları yapamıyor

GetInitialState çalıştırıldıktan sonra api çağrıları yapmanız gerekir. Sizin durumunuz için bunu componentDidMount içinde yapardım. Bu örneği takip edin, https://facebook.github.io/react/tips/initial-ajax.html .

React ile bileşen yaşam döngüsünü de okumanızı tavsiye ederim. https://facebook.github.io/react/docs/component-specs.html .

Değişiklikler ve yükleme durumu ile yeniden yazın

Şahsen ben, oluşturmada mantığı başka bir durumda yapmaktan hoşlanmıyorum ve durumumda 'yükleme'yi kullanmayı ve form yüklenmeden önce harika bir font döndürücü oluşturmayı tercih ediyorum, http://fortawesome.github.io/Font- Harika / örnekler / . İşte size ne demek istediğimi gösterecek bir yeniden yazım. Eğer cjsx için keneleri berbat ettiysem, bunun nedeni normalde sadece kahve kağıdını böyle kullanmamdır.

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>

Bu onu kapatmalı. Durum ve değeri kullanan biçimler hakkında gitmenin bir yolu budur. Ayrıca değer yerine defaultValue kullanabilir ve gönderirken değerleri almak için refs kullanabilirsiniz. Bu rotaya giderseniz, verileri almak ve sonra onu props olarak forma iletmek için bir dış kabuk bileşenine (genellikle yüksek dereceli bileşenler olarak adlandırılır) sahip olmanızı öneririm.

Genel olarak, tepki belgelerini baştan sona okumanızı ve bazı eğitimler yapmanızı öneririm. Orada bir sürü blog var ve http: //www.egghead.io'da bazı iyi öğreticiler vardı. Sitemde de bazı şeyler var, http://www.openmindedinnovations.com .


Başlangıç ​​durumunda API çağrıları yapmanın neden iyi olmadığını merak ediyor musunuz? getInitialState, componentDidMount'tan hemen önce çalıştırılır ve API çağrısı zaman uyumsuzdur. Bu daha geleneksel mi yoksa arkasında başka bir sebep mi var?
Mïchael Makaröv

1
Tam olarak nerede okuduğumu bilmiyorum ama oraya api çağrıları koymadığınızı biliyorum. Bununla başa çıkmak için yapılmış bir kütüphane var, github.com/andreypopp/react-async . Ama bu kitaplığı kullanmazdım ve onu componentDidMount'a yerleştirirdim. Reacts dokümantasyonundaki öğreticide, API'nin componentDidMount'ta da çağırdığını biliyorum.
Blaine Hatab

@ MïchaelMakaröv - çünkü api çağrıları eşzamansızdır ve getInitialState durumu eşzamanlı olarak döndürür. Bu nedenle, api çağrısı tamamlanmadan önce başlangıç ​​durumunuz ayarlanacaktır.
drogon

2
DefaultValue değerini value ile değiştirmek güvenli midir? DefaultValue'nun yalnızca başlatma sırasında yüklendiğini biliyorum, ancak değer de bunu yapıyor gibi görünüyor.
stealthysnacks

2
@stealthysnacks, değeri kullanmakta sorun yok, ancak şimdi girdinin çalışması için bu değeri ayarlamanız gerekiyor. defaultValue sadece başlangıç ​​değerini ayarlar ve giriş değişebilir ancak değer kullanılırken artık 'kontrollüdür'
Blaine Hatab

64

Bunu düzeltmenin başka bir yolu keyda girdinin değiştirilmesidir .

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

Güncelleme: Bu olumlu oylar aldığından, uygun şekilde bir disabledveyareadonly içerik yüklenirken pervaneye , böylece ux deneyimini azaltmazsınız.

Ve evet, büyük olasılıkla bir hack'tir, ama işi bitirir .. ;-)


Saf uygulama - anahtar değeri değiştirilirken değişen bir giriş alanının odak noktası (örneğin, KeyUp'ta çıktı)
Arthur Kushman

2
Evet, bazı dezavantajları var ama işi kolayca hallediyor.
TryingToImprove

Bu akıllıca. Ben için kullandım selectile keyolduğu gibi defaultValuehangi aslında value.
Avi

keygirdinin değiştirilmesi, yeni değeri girdiye yansıtmanın anahtarıdır. Birlikte kullandım type="text"başarıyla.
Jacob Nelson

3

Belki en iyi çözüm değil, ancak aşağıdaki gibi bir bileşen yapardım, böylece kodumun her yerinde yeniden kullanabilirim. Keşke varsayılan olarak zaten tepki vermiş olsaydı.

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

Bileşen şöyle görünebilir:

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()}
    />`

Diziye göre değişiklik, bir dizi tarafından ifade edilen bir yoldan hash'e erişen bir işlevdir.

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 

0

Başlangıç ​​değerlerini belirlemenin en güvenilir yolu, render () {} 'ya ek olarak componentDidMount () {} kullanmaktır:

... 
componentDidMount(){

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

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

Bir öğeyi yok etmeyi ve onu yenisiyle değiştirmeyi kolay bulabilirsiniz.

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

bunun gibi:

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

Ayrıca unmount yönteminin bu sürümünü de kullanabilirsiniz:

ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode);

5
Burada DOM'u kendiniz manipüle ediyorsunuz .. Tepki açısından bu büyük bir HAYIR HAYIR değil mi?
Devashish

0

"PlaceHolder" parametresine değer verin. Örneğin :-

 <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}
    />
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.