Controlled component behaves weird when trying to fill it in, after a validation

Describe the bug
I have a simple functional component form, using MaterialUI and redux state. (For this example, i used local state with the useState() hook. The behavior is the same.)
When trying to save the form that has a field with required validation, for example, the react-hook-form returns the error as expected. However, when I try to type in the field with the error, the first and last character is not inserted.

Is it related to the fact that you are using ref and register in a Controlled component?

To Reproduce
I reproduced this error in the codesandbox. (using the react-hook-form example itself)
The component in question is index.js:

<input
    name="firstName"
    ref={register({
      required: true,
      maxLength: 20,
      pattern: /^[A-Za-z]+$/i
    })}
    onChange={e => setValue(e.target.value)}
    value={value}
  />

Codesandbox link (Required)
Heres a link to reproduce this issue

Expected behavior
I expected that I could type in the field freely, for example, if I remove the value prop in the line 29

Desktop (please complete the following information):

  • OS: iOS
  • Browser chrome
  • Version 89

2 thoughts on “Controlled component behaves weird when trying to fill it in, after a validation

  1. Thanks @bluebill1049

    @adhenrique In case it helps you, here’s how I updated my component based on @bluebill1049‘s tip –

    // Before
    <input
      name="firstName"
      ref={register({
        required: true,
        maxLength: 20,
        pattern: /^[A-Za-z]+$/i
      })}
      onChange={e => setValue(e.target.value)}
      value={value}
    />
    
    // After
    const { control } = useFormContext()
    const {
      field: { ref, ...inputProps },
      meta: { invalid },
    } = useController({
      name: name,
      control,
      rules: { required: required },
      defaultValue: '',
    })
    
    <input
      name={inputProps.name}
      ref={ref}
      onChange={e => {
        setValue(e.target.value)
        inputProps.onChange()
      }}
      onBlur={inputProps.onBlur}
      value={inputProps.value}
    />
  2. Oh, thanks guys. I appreciate that help. In my case i used the <Controller> component:

    <Controller
      name={key}
      control={control}
      defaultValue=""
      rules={rules}
      render={({ onChange, onBlur, name, value, ref }) => (
        <FormFields
          {...parameters}
          name={name}
          inputRef={ref}
          onChange={onChange}
          onBlur={onBlur}
          errors={errors}
          value={value}
        />
      )}
    />