Skip to content

Instantly share code, notes, and snippets.

@JSchaenzle
Last active April 21, 2018 14:47
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save JSchaenzle/3727f5bda08cd6a31e18c24e61c263ce to your computer and use it in GitHub Desktop.
Save JSchaenzle/3727f5bda08cd6a31e18c24e61c263ce to your computer and use it in GitHub Desktop.
React Form Validation
const fieldValidations = [
ruleRunner("name", "Name", required),
ruleRunner("emailAddress", "Email Address", required),
ruleRunner("password1", "Password", required, minLength(6)),
ruleRunner("password2", "Password Confirmation", mustMatch("password1", "Password"))
];
export default class CreateAccount extends React.Component {
constructor() {
// ...
this.state = {
showErrors: false,
validationErrors: {}
};
// Run validations on initial state
this.state.validationErrors = run(this.state, fieldValidations);
}
handleFieldChanged(field) {
return (e) => {
// update() is provided by React Immutability Helpers
// https://facebook.github.io/react/docs/update.html
let newState = update(this.state, {
[field]: {$set: e.target.value}
});
newState.validationErrors = run(newState, fieldValidations);
this.setState(newState);
};
handleSubmitClicked() {
this.setState({showErrors: true});
if($.isEmptyObject(this.state.validationErrors) == false) return null;
// ... continue submiting data to server
}
render() {
return (
<div>
<TextView placeholder="Email address" showError={this.state.showErrors}
text={this.props.emailAddress} onFieldChanged={this.handleFieldChanged("emailAddress")}
errorText={this.errorFor("emailAddress")} />
// Render Name, Password, Submit button, etc. fields
</div>
);
}
}
export const isRequired = fieldName => `${fieldName} is required`;
export const mustMatch = otherFieldName => {
return (fieldName) => `${fieldName} must match ${otherFieldName}`;
};
export const minLength = length => {
return (fieldName) => `${fieldName} must be at least ${length} characters`;
};
export const ruleRunner = (field, name, ...validations) => {
return (state) => {
for (let v of validations) {
let errorMessageFunc = v(state[field], state);
if (errorMessageFunc) {
return {[field]: errorMessageFunc(name)};
}
}
return null;
};
};
export const run = (state, runners) => {
return runners.reduce((memo, runner) => {
return Object.assign(memo, runner(state));
}, {});
};
import * as ErrorMessages from './errorMessages.js';
export const required = (text) => {
if (text) {
return null;
} else {
return ErrorMessages.isRequired;
}
};
export const mustMatch = (field, fieldName) => {
return (text, state) => {
return state[field] == text ? null : ErrorMessages.mustMatch(fieldName);
};
};
export const minLength = (length) => {
return (text) => {
console.log("Checking for length greater than ", length);
return text.length >= length ? null : ErrorMessages.minLength(length);
};
};
import React from 'react';
import OptionallyDisplayed from './OptionallyDisplayed.jsx';
export default class TextField extends React.Component {
constructor(props) {
super(props);
this.shouldDisplayError = this.shouldDisplayError.bind(this);
}
shouldDisplayError() {
return this.props.showError && this.props.errorText != "";
}
render() {
return (
<div>
<input type="text" placeholder={this.props.placeholder}
value={this.props.text} onChange={this.props.onFieldChanged} />
<OptionallyDisplayed display={this.shouldDisplayError()}>
<div className="validation-error">
<span className="text">{this.props.errorText}</span>
</div>
</OptionallyDisplayed>
</div>
);
}
}
TextField.propTypes = {
showError: React.PropTypes.bool.isRequired,
onFieldChanged: React.PropTypes.func.isRequired
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment