React: defaultvalue from state popualted via ajax call

Created on 6 May 2014  路  23Comments  路  Source: facebook/react

getInitialState: function() {
        return {employee: {}};
    },
    componentWillMount: function() {
        this.get("employee/1" , function(data) {this.setState({employee: data})}.bind(this));
    },
    render: function() { 
        console.log(this.state.employee.name);
        return (            
                input type="text" defaultValue={this.state.employee.name} 
                );
    }

In the above code console.log prints fine. But defaultValue is not getting populated!

Probably react already mounted the component before the ajax call. Is there any other way to populate defaultValues from server?

Most helpful comment

I want to chime in here, what I don't really understand is - if I have an input field with a defaultValue={this.state.name} I can change the value in the onClick event like this

handleNameChange:function(e)
{
    this.setState({name:e.target.value});
}

But, if the state is changed elsewhere, the defaultValue does not update. I don't see how this makes sense personally.

If the element gets its defaultValue from the props, it makes no difference, the same problem is faced. Right now I am writing refs for each element that needs to be updated in the form after a state change, and wrapping all of these into a method like this (I'm using jQuery)

forceDefaultValueUpdates:function()
{
    $(React.findDOMNode(this.refs.name)).val(this.state.name);
}

Personally I feel this aspect of form handling in React is a bit clunky.

All 23 comments

If you want more control over an input's value, you probably want to use a controlled component (with value= instead of defaultValue=) as described in this page:

http://facebook.github.io/react/docs/forms.html

You'll need to set an onChange handler and handle changes appropriately.

But I don't need any change handler on any of the controls. I am trying to edit an existing record. And I need to populate current values in to the controls. In this case all I care is to hit save button at the end. Isn't it a common case?

If you're using an uncontrolled component, you can also put a ref on the input and manipulate its value directly. React just gives you a base <input> component which you can build higher-level abstractions over if you need them, but controlled components are applicable as-is to many scenarios. See also:

http://facebook.github.io/react/docs/two-way-binding-helpers.html

hmm.. What is the purpose of defavaulValue? It appears that I can dynamically set its value like the following

componentWillMount: function() {
this.setState({employee: {name: 'foo bar'})}; // if this is via ajax call it doesn't work
},

and then in render function

input type="text" defaultValue={this.state.employee.name}

First of all thanks for the tip.

  1. It looks like any value that need to be set for defaultValue should be available by the time the component mounts. And after that defaultValue is untouched.
  2. html input's 'value' attribute is taken over by React, forcing me to to add a handler, which I don't require in this case.
  3. Now only other way to populate a value in to those fields is via ref value manipulation.

While the following code works,

componentDidMount: function() {
this.get("employee/1" , function(data) {
this.refs.name.getDOMNode().value = data.name; }.bind(this));
},

Imagine the noise it generates if the form has large number of fields!

Now only other way to populate a value in to those fields is via ref value manipulation.

You can also do the following:

componentDidMount: function() {
   var that = this;
   this.get("employee/1" , function(data) {
      that.setState({name: data.name})
   }   
}
...
render: function(){
  (
     <input value={this.state.name} />
  )
}

You can have as many state properties as form fields.

if I use 'value', framework warns me about a missing handler on the input control. I don't need any handlers on those controls.

Ah, I see. I'm curious why defaultValue doesn't show the new state. In your original example, have you tried running forceUpdate() instead of setState()? I have no idea if that would work but it's worth a shot.

defaultValue is read only on initial render, not afterwards. This is because the desired behavior is unclear if the user has changed the field -- do you want to blow away their changes? Usually not.

Is it a bad idea to have and event to give a chance to populate default values? This event should be fired only once at first state change transition

What if someone types into the input field before your ajax call returns? It's unclear to me why you seem to claim that neither controlled components nor manual DOM manipulation work for your use case.

In the following code looks very unnatural for a common case like populating exiting entity from server.

I think this kind of code should be declarative and be in-lined with html tags.

componentDidMount: function() {
this.get("employee/1" , function(data) {
this.refs.fname.getDOMNode().value = data.fname;
this.refs.lname.getDOMNode().value = data.lname;
this.refs.dob.getDOMNode().value = data.dob;
this.refs.address1.getDOMNode().value = data.address1;

// another 20 lines..
},

How would you do this operation without React? It would look roughly the same, right? You can write a wrapper around calls to getDOMNode() to make them more elegant. You could also create a dummy callback and pass them to all input fields, if you wanted to use the value field and setState.

You are right, without react I would have to write the similar code.

Thanks for the discussion and the explanations, we just encountered the same situation.

i'd like to say that if your input refs and field names that you return from your backend match you won't have to write 20 lines and the following will do the work for you: (it's a coffeescript sample though)

$.getJSON("/users/#{@props.userid}").then (user) =>
  for ref of @refs
    @refs[ref].getDOMNode().value = user[ref]

and thanks for this issue, i've learnt that defaultValue is only used once.

I've just experienced this issue, too. The simplest solution for me is to use defaultValue, but defer the rendering of the form until the AJAX request is complete (by adding a loaded property to the component's state).

@juice49 Do you have an example? Thanks!

Maybe something like this?

    render: function () {
        var component;

        if (this.state.productGroup.brand) {
            component = <input type="text" className="form-control" defaultValue={this.state.productGroup.brand} />;
        }
        return (
            <div>
                {component}
            </div>
        );
    }

i have the same problem

And whats up with the situation when I want to have props value in my input:

<input type="text" ref="inputValue" value={this.props.value} onChange={this.handleInputChange} />

How to change the value in my onChange function? I can't change my props value and I can't use state.

I want to chime in here, what I don't really understand is - if I have an input field with a defaultValue={this.state.name} I can change the value in the onClick event like this

handleNameChange:function(e)
{
    this.setState({name:e.target.value});
}

But, if the state is changed elsewhere, the defaultValue does not update. I don't see how this makes sense personally.

If the element gets its defaultValue from the props, it makes no difference, the same problem is faced. Right now I am writing refs for each element that needs to be updated in the form after a state change, and wrapping all of these into a method like this (I'm using jQuery)

forceDefaultValueUpdates:function()
{
    $(React.findDOMNode(this.refs.name)).val(this.state.name);
}

Personally I feel this aspect of form handling in React is a bit clunky.

I was running with the same issue so I ended up doing something like this:

componentDidMount: function() {
   var that = this;
   this.get("employee/1" , function(data) {
      that.setState({data: data, loaded: true})
   }   
},

render: function(){
  (
     {this.state.loaded ? 
     <div>
       <input value={this.state.data.name} />
       <input value={this.state.data.lastname} />
     </div>
     : <p>"Loading..."</p>}
  )
}

@yamill Your method can running, but I bind onChange with form, like so:

onFieldChanged(e) {
        this.setState({
            [e.target.name]: e.target.value
        });
    }

render() {
  <form onChange={::this.onFieldChanged}>
       <input value={this.state.data.name} name="input1" />
       <input value={this.state.data.lastname} name="input2" />
  </form>
}

It's not work, can you give a some advice?THX!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

huxiaoqi567 picture huxiaoqi567  路  3Comments

trusktr picture trusktr  路  3Comments

framerate picture framerate  路  3Comments

zpao picture zpao  路  3Comments

zpao picture zpao  路  3Comments