Asked 1 month ago by SaturnianEngineer533
How can I generically share common react-hook-form fields between Login and Register forms?
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Asked 1 month ago by SaturnianEngineer533
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
I have a login form and a register form (using react-hook-form with React hooks) in my application. Both forms use common fields such as email and password, so I created separate components like emailField and passwordField. The register form reuses the login form fields by extending LoginFormFields (i.e. RegisterFormFields extends LoginFormFields), but I run into a TypeScript error when trying to pass UseFormRegister
I defined emailField.tsx as follows:
TYPESCRIPTinterface EmailFieldParameterTypes { register: UseFormRegister<LoginFormFields>; errors: FieldErrors<LoginFormFields>; }
However, the compiler complains:
TYPESCRIPTType 'UseFormRegister<RegisterFormFields>' is not assignable to type 'UseFormRegister<LoginFormFields>'. Type 'LoginFormFields' is missing the following properties from type 'RegisterFormFields': username, firstname, surnamets(2322) emailField.tsx(5, 5): The expected type comes from property 'register' which is declared here on type 'IntrinsicAttributes & EmailFieldParameterTypes'
This error occurs because even though RegisterFormFields extends LoginFormFields, the generic type UseFormRegister
Below are the referred files:
emailField.tsx
TYPESCRIPTimport { FieldErrors, UseFormRegister } from 'react-hook-form'; import { LoginFormFields } from '../../login/loginForm'; interface EmailFieldParameterTypes { register: UseFormRegister<LoginFormFields>; errors: FieldErrors<LoginFormFields>; } const EmailField = ({ register, errors }: EmailFieldParameterTypes) => { return ( <label className="flex flex-col gap-1"> <div>Email</div> <input className="p-2 border border-gray-500 shadow bg-gray-200" id="email-input" type="email" placeholder={"enter your email"} {...register("email", { required: "email is required!" })} /> {errors.email && <p className="text-red-400">{errors.email.message}</p>} </label> ); }; export default EmailField;
registerForm.tsx
TYPESCRIPTimport { useForm } from "react-hook-form"; import EmailField from "../components/form-fields/emailField"; import PasswordField from "../components/form-fields/passwordField"; import UsernameField from "../components/form-fields/usernameField"; import { LoginFormFields } from "../login/loginForm"; export interface RegisterFormFields extends LoginFormFields { username: string firstname: string surname: string } const RegisterForm = () => { const { register, handleSubmit, formState: { errors }, } = useForm<RegisterFormFields>() const onSubmit = handleSubmit((data) => console.log(data)) return ( <form onSubmit={onSubmit} className="flex w-100 px-12 py-8 flex-col gap-3 max-w-xl shadow 2xl border border-gray-300 rounded-2xl"> <EmailField register={register} errors={errors} /> <PasswordField register={register} errors={errors} /> <UsernameField register={register} errors={errors} /> <button className="bg-blue-400 rounded shadow text-white text-lg p-2"> Giriş Yap </button> </form> ) }; export default RegisterForm;
LoginForm.tsx
TYPESCRIPTimport { useForm } from "react-hook-form"; import EmailField from "../components/form-fields/emailField"; import PasswordField from "../components/form-fields/passwordField"; export interface LoginFormFields { email: string password: string } const LoginForm = () => { const { register, handleSubmit, formState: { errors }, } = useForm<LoginFormFields>() const onSubmit = handleSubmit((data) => console.log(data)) return ( <form onSubmit={onSubmit} className="flex w-100 px-12 py-8 flex-col gap-3 max-w-xl shadow 2xl border border-gray-300 rounded-2xl"> <EmailField register={register} errors={errors} /> <PasswordField register={register} errors={errors} /> <button className="bg-blue-400 rounded shadow text-white text-lg p-2"> Giriş Yap </button> </form> ) }; export default LoginForm;
Comments 0
•Answers 2
•Views 58
Answer by TychoAI Agent • 1 month ago
The issue is that even though RegisterFormFields extends LoginFormFields, the types UseFormRegister
To solve this you can make the EmailField component generic so that it accepts any type that at least has the common fields (i.e. those defined in LoginFormFields). This is typically done by adding a generic parameter to the interface and component. For example:
TYPESCRIPTimport { FieldErrors, UseFormRegister } from 'react-hook-form'; // Restrict T to at least have the "email" property (or extend LoginFormFields) export interface EmailFieldParameterTypes<T extends { email: string }> { register: UseFormRegister<T>; errors: FieldErrors<T>; } const EmailField = <T extends { email: string }>({ register, errors, }: EmailFieldParameterTypes<T>) => { return ( <label className="flex flex-col gap-1"> <div>Email</div> <input className="p-2 border border-gray-500 shadow bg-gray-200" id="email-input" type="email" placeholder="enter your email" {...register("email", { required: "email is required!" })} /> {errors.email && <p className="text-red-400">{errors.email.message}</p>} </label> ); }; export default EmailField;
Now when you use EmailField in your LoginForm or RegisterForm, you pass the specific type:
LoginForm.tsx
TYPESCRIPTimport { useForm } from "react-hook-form"; import EmailField from "../components/form-fields/emailField"; import PasswordField from "../components/form-fields/passwordField"; export interface LoginFormFields { email: string; password: string; } const LoginForm = () => { const { register, handleSubmit, formState: { errors }, } = useForm<LoginFormFields>(); const onSubmit = handleSubmit((data) => console.log(data)); return ( <form onSubmit={onSubmit} className="flex w-100 px-12 py-8 flex-col gap-3 max-w-xl shadow 2xl border border-gray-300 rounded-2xl" > <EmailField<LoginFormFields> register={register} errors={errors} /> <PasswordField<LoginFormFields> register={register} errors={errors} /> <button className="bg-blue-400 rounded shadow text-white text-lg p-2"> Giriş Yap </button> </form> ); }; export default LoginForm;
RegisterForm.tsx
TYPESCRIPTimport { useForm } from "react-hook-form"; import EmailField from "../components/form-fields/emailField"; import PasswordField from "../components/form-fields/passwordField"; import UsernameField from "../components/form-fields/usernameField"; import { LoginFormFields } from "../login/loginForm"; export interface RegisterFormFields extends LoginFormFields { username: string; firstname: string; surname: string; } const RegisterForm = () => { const { register, handleSubmit, formState: { errors }, } = useForm<RegisterFormFields>(); const onSubmit = handleSubmit((data) => console.log(data)); return ( <form onSubmit={onSubmit} className="flex w-100 px-12 py-8 flex-col gap-3 max-w-xl shadow 2xl border border-gray-300 rounded-2xl" > <EmailField<RegisterFormFields> register={register} errors={errors} /> <PasswordField<RegisterFormFields> register={register} errors={errors} /> <UsernameField<RegisterFormFields> register={register} errors={errors} /> <button className="bg-blue-400 rounded shadow text-white text-lg p-2"> Giriş Yap </button> </form> ); }; export default RegisterForm;
By making the field component generic, you allow the register function and errors object to be used with any form model as long as it includes the necessary field(s). This avoids the type incompatibility error since EmailField doesn't force a specific form field type.
Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?
No comments yet.
Answer by UranianPilot452 • 1 month ago
I'd ensure the types for your shared fields are generic, and extend LoginFormFields
.
For example, replacing emailField.tsx
TYPESCRIPTinterface EmailFieldParameterTypes<T extends LoginFormFields> { register: UseFormRegister<T>; errors: FieldErrors<T>; } const EmailField = <T extends LoginFormFields>({ register, errors }: EmailFieldParameterTypes<T>) => { ... };
No comments yet.
No comments yet.