Skip to content

Instantly share code, notes, and snippets.

@granmoe
Last active September 23, 2020 03:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save granmoe/ea267ccb660d04db77b9c362fcd564af to your computer and use it in GitHub Desktop.
Save granmoe/ea267ccb660d04db77b9c362fcd564af to your computer and use it in GitHub Desktop.
Simple react form validation plus a few tiny utilities, via a higher order component
import React from 'react'
export default options => {
return WrappedComponent => class FormValidation extends React.Component {
constructor () {
super()
this.validate = options.validate
this.state = options.fields.reduce((result, field) => {
result.fields.push(field)
result.errors[field] = ''
result.values[field] = ''
result.touched[field] = false
return result
}, { fields: [], errors: {}, values: {}, touched: {} })
this.mapStateToProps.bind(this)
this.runValidate.bind(this)
}
onBlur (field, e) {
var touched = this.state.touched
touched[field] = true
this.setState({ touched: touched })
this.runValidate(field, e.target.value)
}
onChange (field, e) {
this.runValidate(field, e.target.value)
}
runValidate (field, value) {
var values = this.state.values
values[field] = value
this.setState(this.validate(values))
}
mapStateToProps (state) {
return state.fields.reduce((result, field) => {
result[field] = {
value: state.values[field],
error: state.errors[field],
touched: state.touched[field],
onChange: this.onChange.bind(this, field),
onBlur: this.onBlur.bind(this, field)
}
return result
}, {})
}
render () {
return <WrappedComponent {...this.props} {...this.mapStateToProps(this.state)} isValid={this.isValid(this.state)} />
}
isValid (state) {
return state.fields.reduce((previous, current) => previous && (!!state.values[current] && !state.errors[current]), true)
}
getFormData () {
return this.state.values
}
}
}
import React from 'react'
import LoginForm from './login-form.jsx'
export default class LoginFormWrapper extends React.Component {
render () {
return <LoginForm ref='form' onSubmit={this.login.bind(this)}/>
}
login (e) {
e.preventDefault()
const data = this.refs.form.getFormData()
// Do login stuff
}
}
import React from 'react'
import formValidation from './form-validation-and-utils.jsx'
class LoginForm extends React.Component {
render () {
const { username, password, isValid, onSubmit } = this.props
return (
<form onSubmit={onSubmit}>
<input type='text' placeholder='Username' {...username} />
{username.touched && username.error && <div>{username.error}</div>}
<input type='password' placeholder='Password' {...password} />
{password.touched && password.error && <div>{password.error}</div>}
<input type='submit' value='Login' disabled={!this.isValid()} />
</form>
)
}
}
const validate = values => {
let errors = {}
if (!values.username) {
errors.username = 'Required'
} else if (values.username.length > 15) {
errors.username = 'Must be 15 characters or less'
}
if (!values.password) {
errors.password = 'Required'
}
return { errors: errors, values: values }
}
const validationOptions = {
fields: ['username', 'password'],
validate: validate
}
export default formValidation(validationOptions)(LoginForm)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment