JavaScript : Submit a form with reactjs

14 Jul 2015

When ever I read about reactjs, I heard that “Its needs a different thinking”. I understood that statement When I worked on a simple form with loading indicator in reactjs. In usual case I used to add loading class in beforeSend and remove it when $.ajax is done/fail.

But I came to reactjs, first I tried to do the same, but later I understood it should go to state. Here below the code for simple reactjs form. I used loading state to to render loading indicator.

App.Users.Add = React.createClass({
  getInitialState: function () {
    return {username: "", email: "", password: "", loading: false, errors: {}}
  },
  _create: function () {
    return $.ajax({
      url: '/api/users',
      type: 'POST',
      data: {
        username: this.state.username,
        password: this.state.password,
        email: this.state.email
      },
      beforeSend: function () {
        this.setState({loading: true});
      }.bind(this)
    })
  },
  _onSubmit: function (e) {
    e.preventDefault();
    var errors = this._validate();
    if(Object.keys(errors).length != 0) {
      this.setState({
        errors: errors
      });
      return;
    }
    var xhr = this._create();
    xhr.done(this._onSuccess)
    .fail(this._onError)
    .always(this.hideLoading)
  },
  hideLoading: function () {
    this.setState({loading: false});
  },
  _onSuccess: function (data) {
    this.refs.user_form.getDOMNode().reset();
    this.setState(this.getInitialState());
    // show success message
  },
  _onError: function (data) {
    var message = "Failed to create the user";
    var res = data.responseJSON;
    if(res.message) {
      message = data.responseJSON.message;
    }
    if(res.errors) {
      this.setState({
        errors: res.errors
      });
    }
  },
  _onChange: function (e) {
    var state = {};
    state[e.target.name] =  $.trim(e.target.value);
    this.setState(state);
  },
  _validate: function () {
    var errors = {}
    if(this.state.username == "") {
      errors.username = "Username is required";
    }
    if(this.state.email == "") {
      errors.email = "Email is required";
    }
    if(this.state.password == "") {
      errors.password = "Password is required";
    }
    return errors;
  },
  _formGroupClass: function (field) {
    var className = "form-group ";
    if(field) {
      className += " has-error"
    }
    return className;
  },
  render: function() {
    return (
      <div className="form-container">
        <form ref='user_form' onSubmit={this._onSubmit}>
          <div className={this._formGroupClass(this.state.errors.username)}>
            <label className="control-label" for="username">Username </label>
            <input name="username" ref="username" type="text" className="form-control" id="username" placeholder="Username" onChange={this._onChange} />
            <span className="help-block">{this.state.errors.username}</span>
          </div>
          <div className={this._formGroupClass(this.state.errors.email)}>
            <label className="control-label" for="email">Email address</label>
            <input name="email" ref="email" type="email" className="form-control" id="email" placeholder="Email" onChange={this._onChange} />
            <span className="help-block">{this.state.errors.email}</span>
          </div>
          <div className={this._formGroupClass(this.state.errors.password)}>
            <label className="control-label" for="password">Password</label>
            <input name="password" ref="password" type="password" className="form-control" id="password" placeholder="Password" onChange={this._onChange} />
            <span className="help-block">{this.state.errors.password}</span>
          </div>
          <button type="submit" className="btn btn-default" disabled={this.state.loading}>
            Create
            <App.Loading loading={this.state.loading} />
          </button>
        </form>
      </div>
    );
  }
});

So in the above component, I save the value entered in the text boxes into the state and validate which submiting the form. Once I validation is success then I initialte an ajax request. In the beforeSend of I set the loading state to true and whether its fail or success I set the loading state to false. So when the loading state is true it will render the loading component with fontawesome CSS classes.

I extracted the loading component to reuse as much as I can.

App.Loading = React.createClass({
  render: function () {
    if(!this.props.loading) {
      return <span></span>;
    }
    return <span className='fa-spinner fa-spin'></span>
  }
});

Now I can render this App.Users.Add component anywhere.

React.render(<App.Users.Add />, document.getElementById('container'));

I store all the validation errors in the state as well. So when ever the error occurs the reactjs will take care of rending it.

If you particularly enjoy my work, I appreciate donations given with Gittip.
comments powered by Disqus