Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Hook Forms How to pass the errors as a props using Typescript

I'm defining a useForm.

const { handleSubmit, control, errors } = useForm<{email: string}>();

Now I'm creating a seperate component that will the input and I'm going to pass the useForm props i created above.

This how that Components look like.

type Props<T> = {
  name: FieldName<T>;
  control: Control<T>;
  errors: FieldErrors<T>;
};

const ControlTextInput = <T extends {}>({
  name,
  control,
  errors,
}: Props<T>) => {
  return (
   
    <Controller
    name={name}
    control={control}
    rules={{
      required:'this is required',
    }}
    render={({ onChange }) => (
        <>
            <TextInput
                onChangeText={(text) => {
                onChange(text);
                }}
            />
            {/* Show my error here */}
            {errors.email && (
                <Text style={{ color: "red" }}>
                    {errors.email?.message}
                </Text>
            )}
      </>
    )}
  />
  );
};

I want to use the component like this.

   <ControlTextInput<AnyObject>
                    name="email"
                    errors={errors}
                    control={control}
                  />

I get this error when i hover over the errors.email enter image description here

like image 630
Millenial2020 Avatar asked Feb 23 '26 16:02

Millenial2020


1 Answers

React Hook Form exposes type UseControllerProps which accepts generic type T which infers your input value types or in other words the FieldValues type. Initially you define FieldValues type by passing type about your fields to useForm hook (see MyInputTypes below).

interface MyInputTypes {
  email: string;
  password: string;
}

const { register, handleSubmit, watch, formState: { errors } } = useForm<MyInputTypes>();

This means you can create interface which extends UseControllerProps and has your generic T interface Props<T> extends UseControllerProps<T> {}. Type UseControllerProps already includes type definitions for name and control and therefore you will not need to define them separately in your interface (unless you want to or there is a particular requirement / reason to do so). Regarding errors appropriate solution Imo (as you require only error about single field) would be to pass that particular error directly which has type FieldError | undefined. The result looks like below code.

interface Props<T> extends UseControllerProps<T> {
  error: FieldError | undefined
}

Then you can simply use your ControlTextInput as below.

<ControlTextInput name="email" error={errors.email} />

In the Component (which uses ControlTextInput) your generic T must extend FieldValues as eventually, it is this type which infers types about the fields.

As an example ControlTextInput

import React from 'react';
import { Controller, FieldError, FieldValues, UseControllerProps } from 'react-hook-form';

interface Props<T> extends UseControllerProps<T> {
  error: FieldError | undefined;
}

const ControlTextInput = <T extends FieldValues>({ name, control, error }: Props<T>) => {
  return (
    <Controller
      name={name}
      control={control}
      rules={{
        required: 'This is required',
      }}
      render={({ field: { onChange } }) => (
        <>
          <input
            onChange={(text) => {
              onChange(text);
            }}
          />
          {error && <span style={{ color: 'red' }}>{error?.message}</span>}
        </>
      )}
    />
  );
};

export default ControlTextInput;

As an example Component which uses ControlTextInput

import React, { FunctionComponent } from 'react';
import { useForm } from 'react-hook-form';
import ControlTextInput from './ControlTextInput';

interface InputTypes {
  email: string;
  password: string;
}

const Foo: FunctionComponent = () => {
  const {
    formState: { errors },
  } = useForm<InputTypes>();
  return <ControlTextInput name='email' error={errors.email} />;
};

export default Foo;

Here are screenshots with ready code which mimics more or less your approach and solution (same as code above as new to StackOverflow).

ControlTextInput

Component which uses ControlTextInput

like image 178
Matti Kinner Avatar answered Feb 25 '26 12:02

Matti Kinner