Skip to content

Instantly share code, notes, and snippets.

@granmoe
Last active April 3, 2016 00:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save granmoe/056cdadbbf501827e390 to your computer and use it in GitHub Desktop.
Save granmoe/056cdadbbf501827e390 to your computer and use it in GitHub Desktop.
Simple form validation implemented with a higher-order component
import React from 'react'
export default (options, WrappedComponent) => class 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)} />
}
}
import React from 'react'
import formValidation from './form-validation.jsx'
class LoginForm extends React.Component {
render () {
const { username, password } = this.props
return (
<div>
<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>}
</div>
)
}
}
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)
@granmoe
Copy link
Author

granmoe commented Mar 28, 2016

My first stab at a HoC. Any feedback is welcomed :-)

@granmoe
Copy link
Author

granmoe commented Mar 28, 2016

One could also do filtering within the validate function. Although, it would probably be better to expand the HoC to accept a separate filter method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment