Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React-hook-form input fields match validation best practice

What's the best practice when doing input fields match validation when dealing with React-hook-form? For example, when matching email inputs, etc.

While looking into email match validation with React-hook-form found an issue while trying to separate error messages from "coupled elements" through their validation method. The ref only takes one argument that is used for React-hook-form register, while needing to use useRef to access the current.value for value matching, as follows:

import React, { useRef } from "react";
import ReactDOM from "react-dom";
import { useForm } from "react-hook-form";

function App() {
  const { register, handleSubmit, errors } = useForm();
  const inputEmail = useRef(null)
  const onSubmit = data => {
    console.log('onSubmit: ', JSON.stringify(data))
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label htmlFor="email">Email</label>
      <input
        name="email"
        type="email"
        ref={inputEmail}
      />
      {/* desired: show `email` error message */}
      <label htmlFor="email">Email confirmation</label>
      <input
        name="emailConfirmation"
        type="email"
        ref={register({
          validate: {
            emailEqual: value => (value === inputEmail.current.value) || 'Email confirmation error!',
          }
        })}
      />
      {errors.emailConfirmation && <p>{errors.emailConfirmation.message}</p>}
      <input type="submit" />
    </form>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

While this pattern seems to be an option when doing input field matching it does not play well with React-hook-form!

For example, the error message is coupled with one input case only and has no separate messages for each independent field, or one of the input fields does not have the register assigned to it, this means that the property required is not set, etc.

So, I'm looking into a good practice or pattern that solves:

  • Keeping error messages separated by the input field
  • The validation method, when testing the match should be able to reference the twin field value in a React compliant way and not through the DOM (document.querySelector, etc)
like image 908
punkbit Avatar asked Mar 26 '20 18:03

punkbit


People also ask

How do you validate field in React hook form?

=> ({ onChange, onBlur, name, ref }) This method allows you to register an input or select element and apply validation rules to React Hook Form. Validation rules are all based on the HTML standard and also allow for custom validation methods.

Which validation rules are supported by React hook form?

Accessibility (A11y) React Hook Form has support for native form validation, which lets you validate inputs with your own rules. Since most of us have to build forms with custom designs and layouts, it is our responsibility to make sure those are accessible (A11y).

When should you not use a hook on a React?

1. Changing the Hooks Invocation Order. Hooks should not be called within loops, conditions, or nested functions since conditionally executed Hooks can cause unexpected bugs. Avoiding such situations ensures that Hooks are called in the correct order each time the component renders.

Is it okay to call Hooks inside loops or conditions?

Don't call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. By following this rule, you ensure that Hooks are called in the same order each time a component renders.


2 Answers

You shouldn't need the manual ref for inputEmail. Instead, use the getValues method to fetch the current value of your whole form.

const { register, getValues } = useForm()

Then you register both inputs and call getValues from your custom validation.

  <input
    name="email"
    type="email"
    ref={register}
  />
  <input
    name="emailConfirmation"
    type="email"
    ref={register({
      validate: {
        emailEqual: value => (value === getValues().email) || 'Email confirmation error!',
      }
    })}
  />
like image 70
Alex Wayne Avatar answered Jan 11 '23 08:01

Alex Wayne


For this you could use Yup library, which is great:

Add validationSchema to your config object when instantiating useForm and pass a valid Yup schema. Like so:

const Schema = yup.object().shape({
  email: yup.string().required('Required field'),
  emailConfirmation: yup
    .string()
    .oneOf([yup.ref('email')], 'Emails must match')
    .required('Required field'),
});

// How to add it to your useForm
const { register } = useForm({
  validationSchema: Schema
})

Your component should look something like this:

function App() {
  const { register, handleSubmit, errors } = useForm({
    validationSchema: Schema
  });

  const onSubmit = data => {
    console.log('onSubmit: ', JSON.stringify(data))
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label htmlFor="email">Email</label>
      <input
        name="email"
        type="email"
        ref={register}
      />
      {/* desired: show `email` error message */}
      <label htmlFor="email">Email confirmation</label>
      <input
        name="emailConfirmation"
        type="email"
        ref={register}
      />
      {errors.emailConfirmation && <p>{errors.emailConfirmation.message}</p>}
      <input type="submit" />
    </form>
  );
}
like image 24
niconiahi Avatar answered Jan 11 '23 08:01

niconiahi