typescriptreact-hook-formreact-custom-hooks

React Hook Form: How to Avoid Repeatedly Passing Control and Errors Props to Multiple Text Fields?


I am using react-hook-form for validating forms in react. This is how I did it so far. I created a component wrapping a TextField with a react-hook-form Controller component. Then I used in my form.

Component: TextfieldController

import { Controller } from "react-hook-form";

const TextfieldController = ({
    control,
    errors,
    id,
    label,
    placeholder = "",
    size = 50,
    ...otherProps
}: TextfieldControllerProps) => {
    return (
        <Field>
            <Controller
                name={id}
                control={control}
                render={({ field }) => (
                    <TextField
                        {...field}
                        {...otherProps}
                        id={id}
                        label={label}
                        size={size}
                        placeholder={placeholder}
                    />
                )}
            />
            {errors[id] && <Error>{errors[id]?.message?.toString()}</Error>}
        </Field>
    )
}

Usage:

<FormView title="API Artifact" onClose={handleOnClose}>
    <TextfieldController
        required
        autoFocus
        id="apiName"
        label="Name"
        placeholder="Name"
        control={control}
        errors={errors}
    />
    <TextfieldController
        required
        id="apiContext"
        label="Context"
        placeholder="Context"
        control={control}
        errors={errors}
    />
    // continue...
</FormView>

In this case control and errors props should be provided for every usage of TextfieldController component, even both props are same for the entire form. Is there a way to avoid this?

I tried it using a custom hook. Pass control and errors when getting the TextfieldController component from the hook and pass other props when using. But it causes to lose focus on onChange due to re-rendering.

const { TextfieldController } = useTextfieldController({ control, errors });

I don't want to exactly use this custom hook if there is a better way. Is there a better way to do this?


Solution

  • You can use the useFromContext hook to access both control and errors inside your TextfieldController.

    import { Controller, useFormContext } from "react-hook-form";
    
    const TextfieldController = ({
      id,
      label,
      placeholder = "",
      size = 50,
      ...otherProps
    }) => {
      const { formState: { errors }, control } = useFormContext();
    
      return (
        <Field>
          <Controller
            name={id}
            control={control}
            render={({ field }) => (
              <TextField
                {...field}
                {...otherProps}
                id={id}
                label={label}
                size={size}
                placeholder={placeholder}
              />
            )}
          />
          {errors[id] && <Error>{errors[id]?.message?.toString()}</Error>}
        </Field>
      );
    };
    

    As written in the documentation, you will need to wrap your form with a formProvider.
    You could do it for example in your FormView component.