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

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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With