javascriptreactjstypescriptreact-hook-form

Share Form Fields In Different Forms


I have a login and register form(from react hooks) in my application. I want to reuse common fields for both forms.

Therefore, I have created components like emailField, passwordField etc. Since register form contains fields in login form I have derived RegisterFormFields from LoginFormFields. and in emailField.tsx I have defined

interface EmailFieldParameterTypes {
    register: UseFormRegister<LoginFormFields>;
    errors: FieldErrors<LoginFormFields>;
}

where I hope to be able call emailField both with UseFormRegister<RegisterFormFields> and UseFormRegister<LoginFormFields> but I was not. the typescript compiler gave

Type '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'

error even though RegisterFormFields extends LoginFormFields. I have also tried somethings with generics but it didn't workout. appriciated for any help.

referred files:

emailField.tsx

import { 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

import { 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

import { 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;


Solution

  • I'd ensure the types for your shared fields are generic, and extend LoginFormFields.

    For example, replacing emailField.tsx

    interface EmailFieldParameterTypes<T extends LoginFormFields> {
        register: UseFormRegister<T>;
        errors: FieldErrors<T>;
    }
    
    const EmailField = <T extends LoginFormFields>({ register, errors }:
        EmailFieldParameterTypes<T>) => {
    ...
    };