Reaktionseingabe defaultValue wird nicht mit dem Status aktualisiert

94

Ich versuche, ein einfaches Formular mit "Reagieren" zu erstellen, habe jedoch Schwierigkeiten, die Daten ordnungsgemäß an den Standardwert des Formulars zu binden.

Das Verhalten, das ich suche, ist folgendes:

  1. Wenn ich meine Seite öffne, sollte das Texteingabefeld mit dem Text meiner AwayMessage in meiner Datenbank ausgefüllt werden. Das ist "Beispieltext"
  2. Idealerweise möchte ich einen Platzhalter im Texteingabefeld haben, wenn die AwayMessage in meiner Datenbank keinen Text enthält.

Im Moment stelle ich jedoch fest, dass das Texteingabefeld bei jeder Aktualisierung der Seite leer ist. (Obwohl das, was ich in die Eingabe eingebe, ordnungsgemäß gespeichert wird und erhalten bleibt.) Ich denke, dies liegt daran, dass das HTML des Eingabetextfelds geladen wird, wenn die AwayMessage ein leeres Objekt ist, aber nicht aktualisiert wird, wenn die awayMessage geladen wird. Außerdem kann ich keinen Standardwert für das Feld angeben.

Ich habe einen Teil des Codes aus Gründen der Übersichtlichkeit entfernt (z. B. 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>

Mein console.log für AwayMessage zeigt Folgendes:

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

Antworten:

61

defaultValue gilt nur für das anfängliche Laden

Wenn Sie die Eingabe initialisieren möchten, sollten Sie defaultValue verwenden. Wenn Sie jedoch den Status zum Ändern des Werts verwenden möchten, müssen Sie value verwenden. Persönlich verwende ich nur defaultValue, wenn ich es gerade initialisiere, und verwende dann einfach refs, um den Wert beim Senden zu erhalten. Weitere Informationen zu Refs und Eingaben finden Sie in den Reaktionsdokumenten unter https://facebook.github.io/react/docs/forms.html und https://facebook.github.io/react/docs/working-with-the- browser.html .

So würde ich Ihre Eingabe umschreiben:

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

Außerdem möchten Sie Platzhaltertext nicht wie bisher übergeben, da dadurch der Wert auf "Platzhaltertext" gesetzt wird. Sie müssen immer noch einen leeren Wert an die Eingabe übergeben, da undefined und nil den Wert im Wesentlichen in defaultValue umwandeln. https://facebook.github.io/react/tips/controlled-input-null-value.html .

getInitialState kann keine API-Aufrufe tätigen

Sie müssen API-Aufrufe tätigen, nachdem getInitialState ausgeführt wurde. Für Ihren Fall würde ich es in componentDidMount tun. Folgen Sie diesem Beispiel, https://facebook.github.io/react/tips/initial-ajax.html .

Ich würde auch empfehlen, sich mit React über den Komponentenlebenszyklus zu informieren. https://facebook.github.io/react/docs/component-specs.html .

Mit Änderungen und Ladezustand umschreiben

Persönlich mache ich nicht gerne das Ganze, wenn sonst Logik im Rendering und bevorzuge es, "Laden" in meinem Zustand zu verwenden und einen fantastischen Spinner zu rendern, bevor das Formular geladen wird. Http://fortawesome.github.io/Font- Genial / Beispiele / . Hier ist eine Neufassung, um Ihnen zu zeigen, was ich meine. Wenn ich die Zecken für cjsx durcheinander gebracht habe, dann deshalb, weil ich normalerweise nur Coffeescript wie dieses verwende.

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>

Das sollte es ungefähr abdecken. Dies ist eine Möglichkeit, Formulare zu erstellen, die Status und Wert verwenden. Sie können auch nur defaultValue anstelle von value verwenden und dann refs verwenden, um die Werte beim Senden abzurufen. Wenn Sie diesen Weg gehen, würde ich empfehlen, dass Sie eine äußere Hüllenkomponente (normalerweise als Komponenten höherer Ordnung bezeichnet) haben, um die Daten abzurufen und sie dann als Requisiten an das Formular zu übergeben.

Insgesamt würde ich empfehlen, die Reaktionsdokumente vollständig durchzulesen und einige Tutorials durchzuführen. Es gibt viele Blogs und http://www.egghead.io hatte einige gute Tutorials. Ich habe auch einige Dinge auf meiner Website, http://www.openmindedinnovations.com .

Blaine Hatab
quelle
Nur neugierig, warum es nicht gut ist, API-Aufrufe im Ausgangszustand zu machen? getInitialState wird direkt vor componentDidMount ausgeführt, und der API-Aufruf ist asynchron. Ist das konventioneller oder gibt es einen anderen Grund dafür?
Mïchael Makaröv
1
Ich weiß nicht genau, wo ich es gelesen habe, aber ich weiß, dass Sie dort keine API-Anrufe tätigen. Es gibt eine Bibliothek, die dafür erstellt wurde: github.com/andreypopp/react-async . Aber ich würde diese Bibliothek nicht verwenden und sie einfach in componentDidMount platzieren. Ich weiß, dass im Tutorial zu Reacts Documentation der API-Aufruf auch in componentDidMount ausgeführt wird.
Blaine Hatab
@ MïchaelMakaröv - weil API-Aufrufe asynchron sind und getInitialState den Status synchron zurückgibt. Ihr Ausgangszustand wird also eingerichtet, bevor der API-Aufruf abgeschlossen ist.
Drogon
2
Ist es sicher, defaultValue durch value zu ersetzen? Ich weiß, dass defaultValue nur bei der Initialisierung geladen wird, aber value scheint dies auch zu tun.
Stealthysnacks
2
@stealthysnacks Es ist in Ordnung, einen Wert zu verwenden, aber jetzt müssen Sie diesen Wert festlegen, damit die Eingabe überhaupt funktioniert. defaultValue legt nur den Anfangswert fest und die Eingabe kann sich ändern, aber wenn der Wert verwendet wird, wird er jetzt "gesteuert"
Blaine Hatab
59

Eine andere Möglichkeit, dies zu beheben, besteht darin, keydie Eingabe zu ändern .

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

Update: Da dies Upvotes erhält, muss ich sagen, dass Sie eine disabledoder eine readonlyRequisite haben sollten, während der Inhalt geladen wird , damit Sie die UX-Erfahrung nicht verringern.

Und ja, es ist höchstwahrscheinlich ein Hack, aber es erledigt den Job .. ;-)

TryingToImprove
quelle
Naive Implementierung - der Fokus eines Eingabefeldes ändert sich, während der Schlüsselwert geändert wird (ging zum Beispiel auf KeyUp aus)
Arthur Kushman
2
Ja, es hat einige Nachteile, aber es erledigt die Arbeit leicht.
TryingToImprove
Das ist klug. Ich habe es benutzt selectmit keyals defaultValuewas eigentlich ist value.
Avi
Das Ändern der keyEingabe ist der Schlüssel, um den neuen Wert in der Eingabe wiederzugeben. Ich habe es mit type="text"erfolgreich verwendet.
Jacob Nelson
3

Vielleicht nicht die beste Lösung, aber ich würde eine Komponente wie unten erstellen, damit ich sie überall in meinem Code wiederverwenden kann. Ich wünschte, es wäre bereits standardmäßig in Reaktion.

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

Die Komponente kann folgendermaßen aussehen:

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

Wobei Änderung durch Array eine Funktion ist, die über einen von einem Array ausgedrückten Pfad auf Hash zugreift

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
quelle
0

Die zuverlässigste Methode zum Festlegen von Anfangswerten ist die Verwendung von componentDidMount () {} zusätzlich zu render () {}:

... 
componentDidMount(){

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

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

Möglicherweise fällt es Ihnen leicht, ein Element zu zerstören und durch das neue zu ersetzen

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

so was:

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

Sie können auch diese Version der Unmount-Methode verwenden:

ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode);
römisch
quelle
4
Sie manipulieren das DOM hier selbst. Ist das nicht ein großes NEIN NEIN in der Reaktion?
Devashish
0

Geben Sie dem Parameter "placeHolder" einen Wert. Zum Beispiel :-

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