Reputation: 2913
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
Upvotes: 13
Views: 27560
Reputation: 299
Let me show you example how to type props for passing errors
or register
method to child component (ChakraUI based):
interface Props<T extends FieldValues> extends UseControllerProps<T> {
errors: FieldErrors<IFormData>;
register: UseFormRegister<IFormData>;
}
export const Form = <T extends FieldValues>({
register,
errors,
}: Props<T>) => {
Form content:
<FormControl isInvalid={errors?.name? true : false}>
<Input
type="text"
placeholder="Name"
{...register('name', {
required: 'This field is required',
})}
/>
{errors?.modelName && (
<FormErrorMessage>
{errors?.name?.message}
</FormErrorMessage>
)}
</FormControl>
Parent component:
export interface IFormData {
name: string;
}
const {
register,
handleSubmit,
control,
formState: { errors, isSubmitting },
} = useForm<IFormData>({
mode: 'onChange',
});
<form onSubmit={handleUpload}>
<Form
register={register}
errors={errors}
name={''}
/>
</form>
Upvotes: 0
Reputation: 396
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).
Component which uses ControlTextInput
Upvotes: 19
Reputation: 2913
The way I found a solution is to use error type any
errors?: any;
Use the lodash get function
const errName = get(errors, name);
Then i can get the error has follow.
{errName && <Text style={{ color: "red" }}>{errName.message}</Text>}
Upvotes: -2