Yup & React Hook Form: How to validate onChange rather than onSubmit
Asked Answered
P

2

5

I have an onChange function onNameChange that contains a valid variable that should match the yup validation of the name field. The problem is that the valid variable only seems to be correct after submitting the form, not on changing the name field; I want this to be valid before having to submit.

How can I get the value to be correct on changing the name field rather than submitting? Note that I found a similar post but that uses Formik, which is not what I want to use: Formik + Yup: How to immediately validate form before submit?

The Yup settings:

const schema = Yup.object().shape({
    name: Yup.string()
      .required("Required")
      .min(3, "Enter at least 3 characters")
  });
  const {
    register,
    handleSubmit,
    setError,
    formState: { errors },
    trigger
  } = useForm({
    resolver: yupResolver(schema)
    // mode: "onTouched",
    // reValidateMode: "onChange"
  });

The name changing function:

  const onNameChange = async ({ target: { value } }) => {
    const valid = await trigger("name");
    console.log("valid", valid, "value", value);
    if (!valid) {
      // @todo: bug here? valid only correct after submitting
      return;
    }
    getPokemon(value);
    setShowPokemon(false);
  };

The demo form:

<form onSubmit={handleSubmit(onSubmit /*, onError*/)}>
        <input
          {...register("name", { required: true })}
          name="name"
          placeholder="Enter a pokemon"
          onChange={onNameChange}
        />
        <button type="submit" onClick={onSubmit}>
          Show Pokemon
        </button>
        {errors.name && <p>{errors.name.message}</p>}
      </form>

I've made a live demo on codesandbox that should be helpful:

https://codesandbox.io/s/react-playground-forked-odwi2?file=/Pokemon.js

Thanks

Peppy answered 30/7, 2021 at 11:33 Comment(0)
C
7

The problem is that you aren't updating the RHF state after changing your name <input />, because you are overriding the onChange prop, which is returned from {...register('name')}.

So basically you have to options here:

  1. use setValue to update the RHF state value for name inside your onNameChange callback
  2. use <Controller /> component

You can read about it in this discussion on GitHub.

This how it would be implemented for the second option using <Controller />:

<form onSubmit={handleSubmit(onSubmit /*, onError*/)}>
  <Controller
    name="name"
    control={control}
    defaultValue=""
    render={({ field: { value, onChange, ...field } }) => (
      <input
        {...field}
        onChange={({ target: { value } }) => {
          onChange(value);
          onNameChange(value);
        }}
        placeholder="Enter a pokemon"
      />
    )}
  />

  <button type="submit" onClick={onSubmit}>
    Show Pokemon
  </button>
  {errors.name && <p>{errors.name.message}</p>}
</form>

Edit React PlayGround (forked)

Chrome answered 30/7, 2021 at 12:31 Comment(2)
That's a very helpful explanation, thank you. Logging the input using watch also proved that you're rightPeppy
trying to use the <Control /> approach with a selectFlied
P
0

You have to just pass an arg in register function like


    const [credentials, setCredentials] = useState({
        // ....
            emailAddress: '',
        });
      
    const handleChange = (e: any) => {
            setCredentials({
                ...credentials,
                [e.target.name]: e.target.value,
            });
    };

    <div className='md:flex md:items-center mb-6'>
        <Input
            isRequired
            aria-label='emailAddress'
            startContent={<EnvelopeIcon className='h-4 w-6 text-dark' />}
            size={'sm'}
            type='email'
            isInvalid={errors?.emailAddress}
            color={errors?.emailAddress ? 'danger' : 'default'}
                        errorMessage={errors?.emailAddress && errors.emailAddress.message}
            {...register('emailAddress', {
                onChange: (e) => {
                        handleChange(e);},
            })}
            label='Your email address'
            variant='bordered'
            value={credentials.emailAddress}/>
   </div>

Penis answered 19/3 at 7:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.