React input defaultValue doesn't update with state

Neeharika Bhartiya picture Neeharika Bhartiya · May 10, 2015 · Viewed 81.5k times · Source

I'm trying to create a simple form with react, but facing difficulty having the data properly bind to the defaultValue of the form.

The behavior I'm looking for is this:

  1. When I open my page, the Text input field should be filled in with the text of my AwayMessage in my database. That is "Sample Text"
  2. Ideally I want to have a placeholder in the Text input field if the AwayMessage in my database has no text.

However, right now, I'm finding that the Text input field is blank every time I refresh the page. (Though what I type into the input does save properly and persist.) I think this is because the input text field's html loads when the AwayMessage is an empty object, but doesn't refresh when the awayMessage loads. Also, I'm unable to specify a default value for the field.

I removed some of the code for clarity (i.e. 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>

my console.log for AwayMessage shows the following:

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

Answer

TryingToImprove picture TryingToImprove · Jan 18, 2017

Another way of fixing this is by changing the key of the input.

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

Update: Since this get upvotes, I will have to say that you should properly have a disabled or readonly prop while the content is loading, so you don't decrease the ux experience.

And yea, it is most likely a hack, but it gets the job done.. ;-)