Using react-hook-form values as props using Typescript

Viewed 2785

I am new to Typescript and having troubles getting the types to work correctly when I have react-hook-form values as props.

For example, I have a generic Form component that I use for many forms.

interface Props {
  reactHookForm: UseFormMethods<any>
  onSubmit: SubmitHandler<any> 
  isDisabled?: boolean
  isLoading?: boolean
  isReadOnly?: boolean
  children: ReactElement
}

const Form = ({ reactHookForm, onSubmit, isDisabled = false, isLoading = false, isReadOnly = false, children }: Props) => {
  const { handleSubmit, register, errors, control, watch } = reactHookForm
  return (
    <Context.Provider value={{ register, errors, control, watch, isDisabled, isLoading, isReadOnly }}>
      <form onSubmit={handleSubmit(onSubmit)} className="w-full">
        {children}
      </form>
    </Context.Provider>
  )
}

And in an example component I have:

const Auth = ({ onSignIn }: Props) => {
  const signInForm = useForm({ defaultValues, resolver })

  const onSubmit: SubmitHandler<FormData> = (values: FormData) => {
    const { email, password } = values

    const isAuthorized = (user: MyCustomUser) => {
      // do stuff
    }

    const onCompleted = () => {
      // do stuff
    }
    
    const onError = (error: MyCustomError) => {
      // do stuff
    }
    signInWithEmailAndPassword({ email, password, onCompleted, onError, isAuthorized })
  }

  return (
    <section className="flex h-full w-full justify-center items-center bg-gray-100 dark:bg-gray-800">
        {/* some components... */}
        <Form reactHookForm={signInForm} onSubmit={onSubmit} isLoading={attemptingSignIn}>
          {/* form components */}
        </Form>
        {/* some more components ... */}
    </section>
  )
}

and with my FormData, defaultValues and resolver coming from file that looks something like:

import { Resolver } from "react-hook-form"
import { yupResolver } from "@hookform/resolvers/yup"
import { object, SchemaOf, string } from "yup"
import { createInputRequiredError } from "Components/Form/utils"

type FormData = {
  email: string
  password: string
}

const defaultValues: FormData = { email: "", password: "" }

const schema: SchemaOf<FormData> = object().shape({
  email: string().required(createInputRequiredError("email")),
  password: string().required(createInputRequiredError("password"))
})
const resolver = yupResolver(schema)

export type { FormData }
export { defaultValues, resolver }

My problem is that I have to use reactHookForm: UseFormMethods<any> and onSubmit: SubmitHandler<any> with the any type. React hook form has a FieldValues and I thought I could use this by having those types as reactHookForm: UseFormMethods and onSubmit: SubmitHandler<FieldValues> however I get the following error:

Types of parameters 'data' and 'data' are incompatible.
    Type '{ [x: string]: any; }' is missing the following properties from type '{ email: string; password: string; }': email, password

How can I set up the reactHookForm and onSubmit types so that they can be generic to any valid react-hook-form values?


Update

After upgrading my package to the latest version of react-hook-form:7.3.0 I now have a slightly different message (I think these are still equivalent problems).

Type 'UseFormReturn<FormData>' is not assignable to type 'UseFormReturn<FieldValues>'.
  Types of property 'setError' are incompatible.
    Type 'UseFormSetError<FormData>' is not assignable to type 'UseFormSetError<FieldValues>'.

My Form provider was slightly update, it now looks like:

interface Props {
  reactHookForm: UseFormReturn<any>
  onSubmit: SubmitHandler<any>
  isDisabled?: boolean
  isLoading?: boolean
  isReadOnly?: boolean
  children: ReactElement | ReactElement[] | string
}

const Form = ({ reactHookForm, onSubmit, isDisabled = false, isLoading = false, isReadOnly = false, children }: Props) => {
  const {
    handleSubmit,
    register,
    formState: { errors },
    control,
    watch,
    getValues,
    setValue
  } = reactHookForm

  return (
    <Context.Provider value={{ register, errors, control, watch, getValues, setValue, isDisabled, isLoading, isReadOnly }}>
      <form onSubmit={handleSubmit(onSubmit)} className="w-full">
        {children}
      </form>
    </Context.Provider>
  )
}
0 Answers
Related