Last active
December 12, 2023 06:45
-
-
Save thepuskar/4d57dc50558d8104b993b0644ff46f76 to your computer and use it in GitHub Desktop.
React hook form Input custom component with strict typed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Input } from "@/components/input-fields"; | |
import { zodResolver } from "@hookform/resolvers/zod"; | |
import Link from "next/link"; | |
import { SubmitHandler, useForm } from "react-hook-form"; | |
import { z } from "zod"; | |
const RegisterSchema = z | |
.object({ | |
email: z | |
.string() | |
.trim() | |
.min(1, { message: "Email is required" }) | |
.email({ message: "Invalid email address." }), | |
password: z | |
.string({ required_error: "Required error message." }) | |
.trim() | |
.min(1, { message: "Password is required." }) | |
.min(6, { message: "Password must be atleast 6 characters." }) | |
.max(20, { message: "Password must be 20 or fewer characters long." }), | |
confirmPassword: z | |
.string() | |
.trim() | |
.min(1, { message: "Confirm password is required." }), | |
}) | |
.refine( | |
(values) => { | |
return values?.confirmPassword === values?.password; | |
}, | |
{ | |
message: "Password did not match!", | |
path: ["confirmPassword"], | |
} | |
); | |
type RegisterSchemaType = z.infer<typeof RegisterSchema>; | |
export const RegisterForm = () => { | |
const { | |
register, | |
handleSubmit, | |
formState: { errors }, | |
} = useForm<RegisterSchemaType>({ | |
resolver: zodResolver(RegisterSchema), | |
}); | |
const onSubmit: SubmitHandler<RegisterSchemaType> = (data) => { | |
console.log("Submit Successfull", data); | |
}; | |
return ( | |
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm"> | |
<form className="space-y-6" onSubmit={handleSubmit(onSubmit)}> | |
<Input | |
name="email" | |
id="email" | |
type="email" | |
label="Email address" | |
placeholder="Enter email address" | |
register={register} | |
errors={errors} | |
/> | |
<Input | |
name="password" | |
id="password" | |
type="password" | |
label="Password" | |
placeholder="Enter password" | |
register={register} | |
errors={errors} | |
/> | |
<Input | |
name="confirmPassword" | |
id="confirmPassword" | |
type="password" | |
label="Confirm password" | |
placeholder="Confirm password" | |
register={register} | |
errors={errors} | |
/> | |
<div> | |
<button | |
type="submit" | |
className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" | |
> | |
Register | |
</button> | |
</div> | |
</form> | |
<p className="mt-10 text-center text-sm text-gray-500"> | |
Already have an account? | |
<Link | |
href="login" | |
className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500" | |
> | |
{" "} | |
Sign in | |
</Link> | |
</p> | |
</div> | |
); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use client"; | |
import classNames from "classnames"; | |
import { DetailedHTMLProps, InputHTMLAttributes, useState } from "react"; | |
import { FaEye, FaEyeSlash } from "react-icons/fa"; | |
import { | |
UseFormRegister, | |
FieldValues, | |
FieldErrors, | |
Path, | |
RegisterOptions, | |
FieldName, | |
} from "react-hook-form"; | |
import { | |
ErrorMessage, | |
FieldValuesFromFieldErrors, | |
} from "@hookform/error-message"; | |
export type InputType = "text" | "email" | "password"; | |
export type InputProps = { | |
id: string; | |
name: string; | |
label?: string; | |
type?: InputType; | |
className?: string; | |
} & Omit< | |
DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, | |
"size" | |
>; | |
export type FormInputProps<TFormValues extends FieldValues> = { | |
name: Path<TFormValues>; | |
rules?: RegisterOptions; | |
register?: UseFormRegister<TFormValues>; | |
errors?: FieldErrors<TFormValues>; | |
} & Omit<InputProps, "name">; | |
export const Input = <TFormValues extends Record<string, unknown>>({ | |
className, | |
name, | |
register, | |
rules, | |
errors, | |
label, | |
type, | |
...props | |
}: FormInputProps<TFormValues>): JSX.Element => { | |
const [showPassword, setShowPassword] = useState<boolean>(false); | |
const togglePassword = () => { | |
setShowPassword((curr) => !curr); | |
}; | |
return ( | |
<div> | |
<div className="flex items-center justify-between"> | |
<label | |
htmlFor={name} | |
className="block text-sm font-medium leading-6 text-gray-900" | |
> | |
{label} | |
</label> | |
</div> | |
<div className="mt-2 password-div"> | |
<input | |
name={name} | |
type={showPassword ? "text" : type} | |
className={classNames( | |
"block w-full rounded-md border-0 p-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:outline-none focus:ring-gray-600 sm:text-sm sm:leading-6", | |
{ "ring-red-500 focus:ring-red-500": errors?.[name] } | |
)} | |
{...props} | |
{...(register && register(name))} | |
/> | |
{type === "password" ? ( | |
<> | |
{showPassword ? ( | |
<FaEyeSlash className="eyeIcon" onClick={togglePassword} /> | |
) : ( | |
<FaEye className="eyeIcon" onClick={togglePassword} /> | |
)} | |
</> | |
) : null} | |
</div> | |
<ErrorMessage | |
name={ | |
name as unknown as FieldName< | |
FieldValuesFromFieldErrors<FieldErrors<TFormValues>> | |
> | |
} | |
errors={errors} | |
render={({ message }) => ( | |
<div className="text-red-500 mt-0.5 text-xs">{message}</div> | |
)} | |
/> | |
</div> | |
); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment