Important: Typescript ^4.3 above is the recommended version to work with react hook form.
Resolver
import React from 'react'; import { useForm, Resolver } from 'react-hook-form'; type FormValues = { firstName: string; lastName: string; }; const resolver: Resolver<FormValues> = async (values) => { return { values: values.firstName ? values : {}, errors: !values.firstName ? { firstName: { type: 'required', message: 'This is required.', }, } : {}, }; }; export default function App() { const { register, handleSubmit, formState: { errors } } = useForm<FormValues>({ resolver }); const onSubmit = handleSubmit((data) => console.log(data)); return ( <form onSubmit={onSubmit}> <input {...register("firstName")} placeholder="Bill" /> {errors?.firstName && <p>{errors.firstName.message}</p>} <input {...register("lastName")} placeholder="Luo" /> <input type="submit" /> </form> ); }
SubmitHandler
import React from "react"; import { useForm, SubmitHandler } from "react-hook-form"; type FormValues = { firstName: string; lastName: string; email: string; }; export default function App() { const { register, handleSubmit } = useForm<FormValues>(); const onSubmit: SubmitHandler<FormValues> = data => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("firstName")} /> <input {...register("lastName")} /> <input type="email" {...register("email")} /> <input type="submit" /> </form> ); }
Control
import { useForm, useWatch, Control } from "react-hook-form"; type FormValues = { firstName: string; lastName: string; }; function IsolateReRender({ control }: { control: Control<FormValues> }) { const firstName = useWatch({ control, name: "firstName", defaultValue: "default" }); return <div>{firstName}</div>; } export default function App() { const { register, control, handleSubmit } = useForm<FormValues>(); const onSubmit = handleSubmit((data) => console.log(data)); return ( <form onSubmit={onSubmit}> <input {...register("firstName")} /> <input {...register("lastName")} /> <IsolateReRender control={control} /> <input type="submit" /> </form> ); }
UseFormReturn
export type UseFormReturn< TFieldValues extends FieldValues = FieldValues, TContext = any, > = { watch: UseFormWatch<TFieldValues>; getValues: UseFormGetValues<TFieldValues>; getFieldState: UseFormGetFieldState<TFieldValues>; setError: UseFormSetError<TFieldValues>; clearErrors: UseFormClearErrors<TFieldValues>; setValue: UseFormSetValue<TFieldValues>; trigger: UseFormTrigger<TFieldValues>; formState: FormState<TFieldValues>; resetField: UseFormResetField<TFieldValues>; reset: UseFormReset<TFieldValues>; handleSubmit: UseFormHandleSubmit<TFieldValues>; unregister: UseFormUnregister<TFieldValues>; control: Control<TFieldValues, TContext>; register: UseFormRegister<TFieldValues>; setFocus: UseFormSetFocus<TFieldValues>; };
UseFormProps
export type UseFormProps< TFieldValues extends FieldValues = FieldValues, TContext extends object = object > = Partial<{ mode: Mode; reValidateMode: Mode; defaultValues: DeepPartial<TFieldValues>; resolver: Resolver<TFieldValues, TContext>; context: TContext; shouldFocusError: boolean; shouldUnregister: boolean; criteriaMode: 'firstError' | 'all'; }>;
UseFieldArrayReturn
export type UseFieldArrayReturn< TFieldValues extends FieldValues = FieldValues, TFieldArrayName extends FieldArrayPath<TFieldValues> = FieldArrayPath<TFieldValues>, TKeyName extends string = 'id', > = { swap: (indexA: number, indexB: number) => void; move: (indexA: number, indexB: number) => void; prepend: ( value: | Partial<FieldArray<TFieldValues, TFieldArrayName>> | Partial<FieldArray<TFieldValues, TFieldArrayName>>[], options?: FieldArrayMethodProps, ) => void; append: ( value: | Partial<FieldArray<TFieldValues, TFieldArrayName>> | Partial<FieldArray<TFieldValues, TFieldArrayName>>[], options?: FieldArrayMethodProps, ) => void; remove: (index?: number | number[]) => void; insert: ( index: number, value: | Partial<FieldArray<TFieldValues, TFieldArrayName>> | Partial<FieldArray<TFieldValues, TFieldArrayName>>[], options?: FieldArrayMethodProps, ) => void; update: ( index: number, value: Partial<FieldArray<TFieldValues, TFieldArrayName>>, ) => void; replace: ( value: | Partial<FieldArray<TFieldValues, TFieldArrayName>> | Partial<FieldArray<TFieldValues, TFieldArrayName>>[], ) => void; fields: FieldArrayWithId<TFieldValues, TFieldArrayName, TKeyName>[]; };
UseFieldArrayProps
export type UseFieldArrayProps< TKeyName extends string = 'id', TControl extends Control = Control > = { name: string; keyName?: TKeyName; control?: TControl; rules?: Pick< RegisterOptions<TFieldValues>, 'maxLength' | 'minLength' | 'validate' | 'required' >; };
UseControllerReturn
export type UseControllerReturn< TFieldValues extends FieldValues = FieldValues > = { field: ControllerRenderProps<TFieldValues>; fieldState: InputState; };
UseControllerProps
export type UseControllerProps< TFieldValues extends FieldValues = FieldValues > = { name: FieldName<TFieldValues>; rules?: Exclude<RegisterOptions, 'valueAsNumber' | 'valueAsDate' | 'setValueAs' >; onFocus?: () => void; defaultValue?: unknown; control?: Control<TFieldValues>; };
FieldError
export type FieldError = { type: string; ref?: Ref; types?: MultipleFieldErrors; message?: Message; };
FieldErrors
export type FieldErrors< TFieldValues extends FieldValues = FieldValues > = DeepMap<TFieldValues, FieldError>;
Field
export type Field = { ref: Ref; mutationWatcher?: MutationWatcher; options?: RadioOrCheckboxOption[]; } & RegisterOptions;
FieldPath
This type is useful when you define custom component's name
prop, and it will type check again your field path.
export type FieldPath<TFieldValues extends FieldValues> = Path<TFieldValues>;
FieldPathByValue
This type will return union with all available paths that match the passed value
function Field< TFieldValues extends FieldValues, TPath extends FieldPathByValue<TFieldValues, Date> >({ control, name }: { control: Control<TFieldValues>, name: TPath }) { const { field } = useController({ control, name, }); }
FieldValues
export type FieldValues = Record<string, any>;
FieldArrayWithId
export export type FieldArrayWithId< TFieldValues extends FieldValues = FieldValues, TFieldArrayName extends FieldArrayPath<TFieldValues> = FieldArrayPath<TFieldValues>, TKeyName extends string = 'id', > = FieldArray<TFieldValues, TFieldArrayName> & Record<TKeyName, string>;
Mode
export type Mode = { onBlur: 'onBlur'; onChange: 'onChange'; onSubmit: 'onSubmit'; onTouched: 'onTouched'; all: 'all'; };
RegisterOptions
export type RegisterOptions = Partial<{ required: Message | ValidationRule<boolean>; min: ValidationRule<number | string>; max: ValidationRule<number | string>; maxLength: ValidationRule<number | string>; minLength: ValidationRule<number | string>; pattern: ValidationRule<RegExp>; validate: Validate | Record<string, Validate>; }>;
FormStateProxy
export type FormStateProxy<TFieldValues extends FieldValues = FieldValues> = { isDirty: boolean; dirtyFields: Dirtied<TFieldValues>; isSubmitted: boolean; submitCount: number; touched: FieldNames<TFieldValues>; isSubmitting: boolean; isValid: boolean; errors: FieldErrors<TFieldValues>; };
NestedValue (Deprecated at 7.33.0)
import React from 'react'; import { useForm, NestedValue } from 'react-hook-form'; import { Autocomplete, TextField, Select } from '@material-ui/core'; import { Autocomplete } from '@material-ui/lab'; type Option = { label: string; value: string; }; const options = [ { label: 'Chocolate', value: 'chocolate' }, { label: 'Strawberry', value: 'strawberry' }, { label: 'Vanilla', value: 'vanilla' }, ]; export default function App() { const { register, handleSubmit, watch, setValue, formState: { errors } } = useForm<{ autocomplete: NestedValue<Option[]>; select: NestedValue<number[]>; }>({ defaultValues: { autocomplete: [], select: [] }, }); const onSubmit = handleSubmit((data) => console.log(data)); React.useEffect(() => { register('autocomplete', { validate: (value) => value.length || 'This is required.', }); register('select', { validate: (value) => value.length || 'This is required.', }); }, [register]); return ( <form onSubmit={onSubmit}> <Autocomplete options={options} getOptionLabel={(option: Option) => option.label} onChange={(e, options) => setValue('autocomplete', options)} renderInput={(params) => ( <TextField {...params} error={Boolean(errors?.autocomplete)} helperText={errors?.autocomplete?.message} /> )} /> <Select value="" onChange={(e) => setValue('muiSelect', e.target.value as number[])}> <MenuItem value={10}>Ten</MenuItem> <MenuItem value={20}>Twenty</MenuItem> </Select> <input type="submit" /> </form> ); }
Thank you for your support
If you find React Hook Form to be useful in your project, please consider to star and support it.