Forms and validation

Forms and validation

possibilities:

  • use functionality that's built into HTML elements
  • use functionality that's built into component libraries
  • use a form library (react-hook-form, formik)
  • write custom validation code

Forms and validation

built-in functionality of HTML:

<input required={true} minlength={4} />

Forms and validation

When can a form input be validated?

  • when the form is submitted (on submit)
  • when the input loses focus (on blur)
  • on every value change (on change)

The best approach depends on the use case

Validation: examples

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: examples

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
};

Validation: examples

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);

Validation

more complex validation schemas are possible

example: initial validation of an input on its first blur, then live validation on each change

Form libraries

Form libraries

examples:

  • react-hook-form (based on a custom hook)
  • formik (based on custom components)

functionality:

  • validation
  • managing form data
  • simplifying submit handler

react-hook-form

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)

react-hook-form

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

react-hook-form: register

The register function can take some parameters that specify field validation:

  • required
  • min, max
  • minLength, maxLength
  • pattern
  • validate (custom validation function)

react-hook-form: errors

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;

react-hook-form: handleSubmit

handleSubmit will validate form data and pass them to a function if they are valid

<form
  onSubmit={handleSubmit((data) => {
    console.log(data.email);
  })}
>
  ...
</form>

react-hook-form: mode

useForm({ mode: 'onBlur' });

mode: when should the value be validated initially?

  • onSubmit (default)
  • onTouched - when the input loses focus or on submit
  • onBlur - when the input loses focus (does not switch to reValidateMode) or on submit
  • onChange - when the input changes or on submit
  • all - when the input changes or when it loses focus without being changed

react-hook-form: mode

reValidateMode: if the user has submitted the form and there was an error, when should the value be re-validated?

  • onSubmit
  • onBlur
  • onChange (default)

react-hook-form: reset, getValues

const {
  register,
  errors,
  handleSubmit,
  reset,
  getValues,
} = useForm();

formik

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);