1
votes

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

1

1 Answers

1
votes

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)