<main></main>
var Form = React.createClass({
  getInitialState() {
    return { 
      firstName: 'John', 
      lastName: 'Doe', 
      age: 17,
      errors: {},
    };
  },
  
  formData() {
    return {
      firstName: this.state.firstName,
      lastName: this.state.lastName,
      age: this.state.age,
    }
  },
  
  formSchema() {
    return {
      firstName: { presence: true },
      lastName: { presence: true },
      age: { 
        presence: true,
        numericality: {
          greaterThanOrEqualTo: 0,
          lessThanOrEqualTo: 120,
        },
      }
    }
  },
  
  set(key, val) {
    this.setState({ [key]: val });
  },
  
  validate(ev) {
    ev.preventDefault();
    this.setState({ errors: validate(this.formData(), this.formSchema())});
  },
  
  renderEntry(key, label, type='text') {
    const errors = this.state.errors || {};
    const errorMessages = errors[key];
    return (<div className="field-data">
        <label>
          {label}
          <input 
            type={type} 
            value={this.state[key]} 
            onChange={(ev) => this.set(key, ev.target.value)} />
          
          {errorMessages && <span class="error">{errorMessages}</span>}
        </label>
      </div>);  
  },
  
  render: function() {
    return (<form onSubmit={this.validate}>
        {this.renderEntry('firstName', 'First Name')}
        {this.renderEntry('lastName', 'Last Name')}
        {this.renderEntry('age', 'Age', 'number')}
        <input type="submit" value="save" />
    </form>);
  }
});

ReactDOM.render(<Form />, document.querySelector('main'));
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://fb.me/react-15.1.0.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/validate.js/0.11.1/validate.min.js
  3. https://fb.me/react-dom-15.1.0.min.js