skip to Main Content

I have a form (codesandbox here) with 2 components:

  • A select input for the language (e.g.: English, Spanish)
  • A textfield where the user enters a text in the selected language

image

The default form data looks like this:

{
  "greeting": {
    "en-US": "Hello",
    "es-ES": "Hola"
  }
}

My initial idea was to have a single input and just change its registered name based on the language selected. This way, when I select es-ES, react-hook-form should replace the input’s value with the spanish text, and update the es-ES prop when the user edits the text. However, if you play with the codesandbox above, you’ll notice that the wrong prop is being used.

I did find a potential workaround, which is to wrap my input with RHF’s <Controller /> and override the onChange and value, but that would likely require a considerable effort to implement on my real project, as the input is already using the Controller internally.

Hence my question: is this the expected behaviour of the library? If so, how would you approach this UI instead?

2

Answers


  1. This bug may be caused by ref approach react-hook-form and their render optimisations. If I were you, I pass key prop to Controller for re-render.

     <Controller
               key={fieldName} // <-- add key prop depending fieldName
               name={fieldName}
               control={control}
               render={({ field: { ref, ...fieldProps }, fieldState: { error } }) => (
                 <TextField
                   label={fieldName}
                   InputProps={{
                     endAdornment: <div><LanguagePicker value={locale} onChange={setLocale} /></div>
                   }}
                   variant="outlined"
                   inputRef={ref}
                   {...fieldProps}
                 />
               )}
             />
    

    With this workaround, you are sure that the controller render again if the key changed.

    Edit: I’m not sure but this code block from the library repo looks like the root cause of this issue. They create registerProps with ref and your dynamic fieldName can’t be updated in there.

    Login or Signup to reply.
  2. Yes, this is the expected behavior of React Hook Form. When you register an input with a name, React Hook Form will create a unique field path for that input. The field path is a string that uniquely identifies the input within the form. In your case, the field path for the input would be greeting.{language}, where {language} is the value of the select input.

    When you change the value of the select input, React Hook Form will update the field path for the input. This is why the wrong prop is being used when you select es-ES. The field path for the input is now greeting.es-ES, but the input is still registered with the name greeting.

    To fix this, you need to wrap the input with the Controller component and pass the name prop to the Controller component. This will tell React Hook Form to use the name prop as the field path for the input.

      const Input = ({ name, ...rest }) => {
      const { control, formState } = useForm();
    
      const [value, setValue] = useState(formState.getFieldValue(name));
    
      return (
        <Controller
          name={name}
          control={control}
          value={value}
          onChange={setValue}
          {...rest}
        />
      );
    };
    

    Another way to approach this UI is to use two separate inputs, one for the language and one for the text. This would be the simplest solution, but it would also require more code.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search