A minimal react form with data parsing and input validation.

by marhaba

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react-dom.min.js"></script>
<div id="app"></div>

SCSS

body {
  font-family: 'Roboto Slab', sans-serif;
  background: white;
  color: #484848;
  padding: 40px;
}

input {
  display: block;
  margin-bottom: 15px;
  margin-top: 5px;
  padding: 10px;
  border: 1px solid #cfcfcf;
  font-family: 'Roboto Slab', sans-serif;
  font-size: 16px;
  outline: none;
}

pre, code {
  font-family: 'Roboto Mono', Monaco;
}

button {
  cursor: pointer;
  padding: 12px;
  background: #999;
  font-family: 'Roboto Slab', sans-serif;
  font-size: 16px;
  border: none;
  outline: none;
  color: white;
  border-bottom: 2px solid #797979;
  
  &:hover {
    background-color: #a1a1a1;
  }

  &:active {
    background-color: #888;
  }
}

form {
  display: inline-block;
  margin-right: 50px;
  vertical-align: top;
}

.res-block {
  display: inline-block;
}

h3 {
  margin-top: 0;
}

@keyframes bounce {
  from, 20%, 53%, 80%, to {
    animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
    transform: translate3d(0,0,0);
  }

  40%, 43% {
    animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
    transform: translate3d(0, -30px, 0);
  }

  70% {
    animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
    transform: translate3d(0, -15px, 0);
  }

  90% {
    transform: translate3d(0,-4px,0);
  }
}

.bounce {
  animation: bounce 0.5s;
  transform-origin: center bottom;
}

Babel + JSX

const inputParsers = {
  date(input) {
    const split = input.split('/');
    const day = split[1]
    const month = split[0];
    const year = split[2];
    return `${year}-${month}-${day}`;
  },
  uppercase(input) {
    return input.toUpperCase();
  },
  number(input) {
    return parseFloat(input);
  },
};

class ShakingError extends React.Component {
	constructor() { super(); this.state = { key: 0 }; }

	componentWillReceiveProps() {
    // update key to rerender the component to rerun the animation
  	this.setState({ key: ++this.state.key });
  }
  
  render() {
  	return <div key={this.state.key} className="bounce">{this.props.text}</div>;
  }
}

class MyForm extends React.Component {
  constructor() {
    super();
    this.state = {};
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(event) {
    event.preventDefault();
    if (!event.target.checkValidity()) {
    	this.setState({ invalid: true });
      return;
    }
    const form = event.target;
    const data = new FormData(form);

    for (let name of data.keys()) {
      const input = form.elements[name];
      const parserName = input.dataset.parse;
      console.log('parser name is', parserName);
      if (parserName) {
        const parsedValue = inputParsers[parserName](data.get(name))
        data.set(name, parsedValue);
      }
    }
    
    this.setState({
    	res: stringifyFormData(data),
      invalid: false,
    });

    // fetch('/api/form-submit-url', {
    //   method: 'POST',
    //   body: data,
    // });
  }

  render() {
  	const { res, invalid } = this.state;
    return (
    	<div>
        <form onSubmit={this.handleSubmit} noValidate>
          <label htmlFor="username">Username:</label>
          <input
            id="username"
            name="username"
            type="text"
            data-parse="uppercase" required
          />

          <label htmlFor="email">Email:</label>
          <input id="email" name="email" type="email" />

         ...