Redux Form
Even though revalidate is agnostic about how you use it, it does work out of the
box for Redux Form. The validate function you might write for a Redux Form
example like here can
also be automatically generated with combineValidators. The function it
returns will work perfectly for the validate option for your form components
for React and Redux Form.
Here is that example from Redux Form rewritten to generate a validate function
with revalidate for both Redux Form v6 and v5.
Redux Form v6
import React from 'react'
import { Field, reduxForm } from 'redux-form'
import {
createValidator,
composeValidators,
combineValidators,
isRequired,
hasLengthLessThan,
isNumeric
} from 'revalidate'
const isValidEmail = createValidator(
message => value => {
if (value && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) {
return message
}
},
'Invalid email address'
)
const isGreaterThan = (n) => createValidator(
message => value => {
if (value && Number(value) <= n) {
return message
}
},
field => `${field} must be greater than ${n}`
)
const customIsRequired = isRequired({ message: 'Required' })
const validate = combineValidators({
username: composeValidators(
customIsRequired,
hasLengthLessThan(16)({
message: 'Must be 15 characters or less'
})
)(),
email: composeValidators(
customIsRequired,
isValidEmail
)(),
age: composeValidators(
customIsRequired,
isNumeric({
message: 'Must be a number'
}),
isGreaterThan(17)({
message: 'Sorry, you must be at least 18 years old'
})
)()
})
const warn = values => {
const warnings = {}
if (values.age < 19) {
warnings.age = 'Hmm, you seem a bit young...'
}
return warnings
}
const renderField = ({ input, label, type, meta: { touched, error, warning } }) => (
<div>
<label>{label}</label>
<div>
<input {...input} placeholder={label} type={type}/>
{touched && ((error && <span>{error}</span>) || (warning && <span>{warning}</span>))}
</div>
</div>
)
const SyncValidationForm = (props) => {
const { handleSubmit, pristine, reset, submitting } = props
return (
<form onSubmit={handleSubmit}>
<Field name="username" type="text" component={renderField} label="Username"/>
<Field name="email" type="email" component={renderField} label="Email"/>
<Field name="age" type="number" component={renderField} label="Age"/>
<div>
<button type="submit" disabled={submitting}>Submit</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>
Clear Values
</button>
</div>
</form>
)
}
export default reduxForm({
form: 'syncValidation', // a unique identifier for this form
validate, // <--- validation function given to redux-form
warn // <--- warning function given to redux-form
})(SyncValidationForm)
Redux Form v5
import React, {Component, PropTypes} from 'react';
import {reduxForm} from 'redux-form';
import {
createValidator,
composeValidators,
combineValidators,
isRequired,
hasLengthLessThan,
isNumeric
} from 'revalidate';
export const fields = ['username', 'email', 'age'];
const isValidEmail = createValidator(
message => value => {
if (value && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) {
return message;
}
},
'Invalid email address'
);
const isGreaterThan = (n) => createValidator(
message => value => {
if (value && Number(value) <= n) {
return message;
}
},
field => `${field} must be greater than ${n}`
);
const customIsRequired = isRequired({ message: 'Required' });
const validate = combineValidators({
username: composeValidators(
customIsRequired,
hasLengthLessThan(16)({
message: 'Must be 15 characters or less'
})
)(),
email: composeValidators(
customIsRequired,
isValidEmail
)(),
age: composeValidators(
customIsRequired,
isNumeric({
message: 'Must be a number'
}),
isGreaterThan(17)({
message: 'Sorry, you must be at least 18 years old'
})
)()
});
class SynchronousValidationForm extends Component {
static propTypes = {
fields: PropTypes.object.isRequired,
handleSubmit: PropTypes.func.isRequired,
resetForm: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired
};
render() {
const {fields: {username, email, age}, resetForm, handleSubmit, submitting} = this.props;
return (<form onSubmit={handleSubmit}>
<div>
<label>Username</label>
<div>
<input type="text" placeholder="Username" {...username}/>
</div>
{username.touched && username.error && <div>{username.error}</div>}
</div>
<div>
<label>Email</label>
<div>
<input type="text" placeholder="Email" {...email}/>
</div>
{email.touched && email.error && <div>{email.error}</div>}
</div>
<div>
<label>Age</label>
<div>
<input type="text" placeholder="Age" {...age}/>
</div>
{age.touched && age.error && <div>{age.error}</div>}
</div>
<div>
<button type="submit" disabled={submitting}>
{submitting ? <i/> : <i/>} Submit
</button>
<button type="button" disabled={submitting} onClick={resetForm}>
Clear Values
</button>
</div>
</form>
);
}
}
export default reduxForm({
form: 'synchronousValidation',
fields,
validate
})(SynchronousValidationForm);