fungusanthrax
fungusanthrax

Reputation: 190

Input fields do not respond to changing state to empty object

How come after I post something, the input fields' values remain the same, even after I change the state of data to null? I console.logged the state and after a post the data state variable is correctly set to an empty object, but the state of the inputs don't change. Please advise.

var AccountView = React.createClass({

getDefaultProps: function() {
    return {
        data: {},
        users: [],
    };
},

getInitialState: function() {
    return {
        data: {},
        users: {},
    };
},

componentDidMount: function() {
    $.ajax({
        url: "/react-webpack/js/source/controllers/get_accounts.php",
        async: "false",
        dataType: "json",
        sucess: function(json) {
            console.log("success!");
            console.log(json);      

        },
        error:function(x, e) {
            alert(e);
        },
        complete: function(a, data) {
            var users = JSON.parse(a.responseText);
            this.setState({
                users: users,
            });
        }.bind(this)
    });
},

handleChange: function(e) {
    var data = this.state.data;
    data[e.target.name] = e.target.value;
    this.setState({
        data: data,
    });
},

handleSave: function(e) {
    e.preventDefault();
    var users = this.state.users;
    users.push(this.state.data);
    $.ajax({
        url: "/react-webpack/js/source/controllers/post_accounts.php",
        type: "post",
        data: this.state.data,
        success: function(data, a) {
            this.setState({
                data: {},
            });
        }.bind(this),
        error: function(e) {

        },
        complete: function(a, data) {
            this.setState({
                data: {},
                users: users,
            });
        }.bind(this)
    });
},

render: function() {
    return (
        <div className="container">
            <form method="post">    
                <dl id="form" className="row">
                    <div className="col-md-4">
                        <dt>Firstname</dt>
                        <dd>
                            <input name="firstname" type="text" value={this.state.data.firstname} onChange={this.handleChange} />
                        </dd>
                        <dt>Lastname</dt>
                        <dd>
                            <input name="lastname" type="text" value={this.state.data.lastname} onChange={this.handleChange} />
                        </dd>
                        <dt>Password</dt>
                        <dd>
                            <input name="password" type="text" value={this.state.data.password} onChange={this.handleChange} />
                        </dd>
                        <dt>Email Address</dt>
                        <dd>
                            <input name="emailaddress" type="text" value={this.state.data.emailaddress} onChange={this.handleChange} />
                        </dd>
                        <dt>Submit</dt>
                        <dd>
                            <input type="submit" value="Submit" onClick={this.handleSave} />
                        </dd>
                    </div>
                    <div className="col-md-8">
                        <table>
                            <thead>
                                <tr>
                                    <th>Firstname</th>
                                    <th>Lastname</th>
                                    <th>Password</th>
                                    <th>Email address</th>
                                </tr>
                            </thead>
                            <tbody>
                            {_.map(this.state.users, function(user) {
                            return (
                                    <tr>
                                        <td>{user.firstname}</td>
                                        <td>{user.lastname}</td>
                                        <td>{user.password}</td>
                                        <td>{user.emailaddress}</td>
                                    </tr>
                                    );  
                            })}
                            </tbody>
                        </table>
                    </div>
                </dl>
            </form>
        </div>
    );
}

});

module.exports = AccountView;

Upvotes: 1

Views: 455

Answers (2)

fungusanthrax
fungusanthrax

Reputation: 190

This is what I found to work based off of James Ganong's work, although note in my work environment setting the state to clear isn't necessary where the problem: null object not clearing the inputs doesn't exist.

handleSave: function(e) {
    e.preventDefault();
    var users = this.state.users;
    users.push(this.state.data);
    $.ajax({
    url: "/react-webpack/js/source/controllers/post_accounts.php",
    type: "post",
    data: this.state.data,
    success: function(data, a) {
        this.setState({
            data: {},
        });
    }.bind(this),
    error: function(e) {

    },
    complete: function(a, data) {
       var clear = {
            firstname: '',
            lastname: '',
            password: '',
            emailaddress: '',
       };
       this.setState({
            data: clear,
            users: users,
        });
    }.bind(this)
});
},

Upvotes: 1

James Ganong
James Ganong

Reputation: 1171

When you use value={this.state.data.firstname}on an input, if this.state.data.firstname is null or undefined and there is already a value set on the input, it keeps it. So when you set data to an empty object, everything is passing null/undefined to the inputs.

So, when you update your state, instead of setting data to an empty object, reset all the values to an empty string. A good way to do this is to first create a new initialState var outside of your class with the full shape of the state and then use it in your getInitialState method:

var initialState = {
    data: {
      firstname: '',
      lastname: '',
      password: '',
      emailaddress: ''
    },
    users: [] // also not this should be an array instead of an object
};

Inside your class:

getInitialState: function() {
  // we're cloning initialState so it doesn't get modified directly
  // as we want to use it later to reset the state
  return Object.assign({}, initialState); 
},

Next, instead of doing this.setState({ data: {} }) do this:

this.setState({
  data: Object.assign({}, initialState.data)
});

On a side note, you also have two places where you're mutating the state directly - handleChange and handleSave:

handleChange: function(e) {
    var data = this.state.data;
    data[e.target.name] = e.target.value; // this is directly mutating the state
    this.setState({
        data: data,
    });
},

// ...

handleSave: function(e) {
  e.preventDefault();
  var users = this.state.users;
  users.push(this.state.data); // this is directly mutating the state
  // ...

Mutating the state outside of setState() can cause some weird unintended side effects and generally is a cause of poor performance.

To fix it, you first copy the state data, add to it, then set it (only requires simple changes):

handleChange: function(e) {
    var data = Object.assign({}, this.state.data);
    data[e.target.name] = e.target.value;
    this.setState({
        data: data,
    });
},

// ...

handleSave: function(e) {
  e.preventDefault();
  var users = this.state.users.slice();
  users.push(this.state.data);
  // ...

Upvotes: 0

Related Questions