Skip to content

Instantly share code, notes, and snippets.

@prestonp
Last active July 20, 2016 22:15
Show Gist options
  • Save prestonp/ef6d17c6b3ddf1e6567631ffb54365df to your computer and use it in GitHub Desktop.
Save prestonp/ef6d17c6b3ddf1e6567631ffb54365df to your computer and use it in GitHub Desktop.
react validation
class TestForm extends React.Component {
constructor() {
super();
this.state = {
name: '',
address: '',
};
this.nameOnChange = this.nameOnChange.bind(this);
this.addressOnChange = this.addressOnChange.bind(this);
this.validate = this.validate.bind(this);
}
nameOnChange(e) {
this.setState({ name: e.target.value });
}
addressOnChange(e) {
this.setState({ address: e.target.value });
}
validate() {
// This validation schema could be anything but should follow this object pattern
// validationErrors = { input => [error, error, error, ... ] }
// Leaving validate() implementation up to the component allows users
// to use whatever validation lib they want
let validationErrors = { name: [], address: [] };
const { name, address } = this.state;
if (name.length < 5) {
validationErrors.name.push('Name must be atleast 5 characters');
}
if (name.indexOf('james') >= 0) {
validationErrors.name.push('Name cannot contain "james"');
}
if (address.length < 5) {
validationErrors.address.push('Address must be atleast 5 characters');
}
if (name === address) {
validationErrors.address.push('Address must not be the same as name');
}
this.props.setValidationErrors(validationErrors);
}
render() {
return <div>
<label>Name</label>
<input
value={this.state.name}
onChange={this.nameOnChange}
className={this.props.getClassName('name')}
onBlur={this.validate} />
<ValidationErrors errors={this.props.validationErrors['name'] || []} />
<label>Address</label>
<input
value={this.state.address}
onChange={this.addressOnChange}
className={this.props.getClassName('address')}
onBlur={this.validate}/>
<ValidationErrors errors={this.props.validationErrors['address'] || []} />
</div>;
}
}
module.exports = Validate(TestForm);
/**
* Higher order component for maintaining validation state and utility
* functions for accessing errors and decorating className
*/
const Validate = ComposedComponent => class extends React.Component {
constructor() {
super();
this.state = {
validationErrors: {}
};
this.getClassName = this.getClassName.bind(this);
this.setValidationErrors = this.setValidationErrors.bind(this);
}
/**
* getClassName returns the className based on an input
* @param {string} name - input name
* @param {string} className - the original className
* @return {string} the className with 'invalid' if there is a validation
* error present for the given input name
*/
getClassName(name, className='') {
const present = this.state.validationErrors[name];
if (!present) return className;
const valid = !this.state.validationErrors[name].length;
if (valid) return className;
return (className + ' ' + 'invalid').trim();
}
/**
* setValidationErrors sets the validation errors
* @param {object} validationErrors - object, keyed by input names associated
with the list of error strings
*/
setValidationErrors(validationErrors) {
this.setState({ validationErrors });
}
render() {
return <ComposedComponent {...this.props} {...this.state}
getClassName={this.getClassName}
setValidationErrors={this.setValidationErrors} />;
}
};
function ValidationErrors({ errors }) {
const errs = errors.map((err, idx) => {
return <li key={idx}>{err}</li>;
});
return <ul>{errs}</ul>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment