possibilities:
built-in functionality of HTML:
<input required={true} minlength={4} />
When can a form input be validated?
The best approach depends on the use case
validation on every change:
const NewsletterSignup = () => {
const [email, setEmail] = useState('');
const emailValid = isEmail(email);
// ...
// display a form
// and optionally a warning about an invalid email
};
validation on blur:
const NewsletterSignup = () => {
const [email, setEmail] = useState('');
const [emailValid, setEmailValid] = useState(true);
// call from onBlur:
const validateEmail = () => {
setEmailValid(isEmail(email));
};
// ...
// display a form
// and optionally a warning about an invalid email
};
full example: validation on blur and on submit
const NewsletterSignup = () => {
const [email, setEmail] = useState('');
const [emailValid, setEmailValid] = useState(true);
const validateEmail = () => {
setEmailValid(isEmail(email));
};
return (
<form
onSubmit={(event) => {
event.preventDefault();
validateEmail();
if (isEmail(email)) {
console.log(`Signed up: ${email}`);
}
}}
>
<input
value={email}
onChange={(event) => setEmail(event.target.value)}
onBlur={() => validateEmail()}
/>
<button>sign up</button>
{!emailValid ? <div>invalid email</div> : null}
</form>
);
};
const isEmail = (email) =>
email.match(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i);
more complex validation schemas are possible
example: initial validation of an input on its first blur, then live validation on each change
examples:
functionality:
react-hook-form does not keep input contents in React state
advantages: faster, simpler
disadvantages: deviates from standard React concepts (uses refs instead of state)
import { useForm } from 'react-hook-form';
function NewsletterSignup() {
const { register, handleSubmit, formState } = useForm();
return (
<form onSubmit={handleSubmit(console.log)}>
<input
{...register('email', {
required: true,
pattern: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
})}
/>
{formState.errors.email ? (
<div>invalid email</div>
) : null}
<button type="submit">sign up for newsletter</button>
</form>
);
}
Note: react-hook-form uses a callback ref to access the input
The register
function can take some parameters that specify field validation:
required
min
, max
minLength
, maxLength
pattern
validate
(custom validation function)formState.errors
indicates errors for any registered input
<input {...register('email')} />
formState.errors.email ? <div>invalid email</div> : null;
!formState.isValid ? <div>invalid form entries</div> : null;
handleSubmit
will validate form data and pass them to a function if they are valid
<form
onSubmit={handleSubmit((data) => {
console.log(data.email);
})}
>
...
</form>
useForm({ mode: 'onBlur' });
mode
: when should the value be validated initially?
onSubmit
(default)onTouched
- when the input loses focus or on submitonBlur
- when the input loses focus (does not switch to reValidateMode
) or on submitonChange
- when the input changes or on submitall
- when the input changes or when it loses focus without being changedreValidateMode
: if the user has submitted the form and there was an error, when should the value be re-validated?
onSubmit
onBlur
onChange
(default)const {
register,
errors,
handleSubmit,
reset,
getValues,
} = useForm();
import { Formik, Form, Field, ErrorMessage } from 'formik';
const NewsletterRegistration = () => (
<Formik
initialValues={{ email: '' }}
onSubmit={(values) => console.log(values)}
validate={(values) => {
const errors = {};
if (!isEmail(values.email)) {
errors.email = 'invalid email';
}
return errors;
}}
>
{(props) => (
<Form>
<Field type="email" name="email" />
<button type="submit" disabled={!props.isValid}>
subscribe
</button>
<ErrorMessage name="email" component="div" />
</Form>
)}
</Formik>
);
const isEmail = (email) =>
email.match(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i);