들어가며
react-hook-form
과 커스텀 Input컴포넌트를 사용하다보니, 커스텀 컴포넌트가 특정 Props에 종속되는 상황이 발생했다.
import { TSignUpProps } from "@/app/(account)/sign-up/page";
import React from "react";
import { Path, UseFormRegister } from "react-hook-form";
type TInputProps = {
label: Path<TSignUpProps>;
register: UseFormRegister<TSignUpProps>;
} & React.InputHTMLAttributes<HTMLInputElement>;
const Input = (props: TInputProps) => {
...
return (...)
};
export default Input;
- 위 경우, Input컴포넌트는
TSignUpProps
에 해당하는 props만 받게 되어 재사용이 불가능한 종속적인 컴포넌트
가 되어버린다.
해결법 (제네릭)
- Input컴포넌트의 재사용을 위해 T~~Props를 제네릭으로 받아 사용할 수 있게 바꾸어보았다.
import { TSignUpProps } from "@/app/(account)/sign-up/page";
import React from "react";
import {
Path,
UseControllerProps,
UseFormRegister,
useController,
FieldValues,
} from "react-hook-form";
type TControlProps<T extends FieldValues> = UseControllerProps<T> & {};
const Control = <T extends FieldValues>({ ...props }: TControlProps<T>) => {
const { field } = useController(props);
const inputRef = React.useRef<HTMLInputElement>(null);
const onclick = () => {
inputRef.current?.focus();
};
return (
<div
className="flex flex-col bg-primary-100 p-3 cursor-text"
onClick={onclick}
>
<label className="cursor-text" htmlFor={props.name}>
{props.name.toUpperCase()}
</label>
<input
{...field}
ref={inputRef}
placeholder={props.name}
className="bg-primary-100 outline-none"
/>
</div>
);
};
const Input = {
Control,
};
export default Input;
- 위와 같이 수정된 Input컴포넌트는, 사용하는 화면에서
type
을 지정하여 사용할 수가 있게 된다.
<Input.Control<TSignUpProps>
name={}
control={}
</Input>
"use client";
import React from "react";
import Input from "@/components/Input";
import Button from "@/components/Button";
import { Controller, SubmitHandler, useForm } from "react-hook-form";
export type TSignUpProps = {
email: string;
password: string;
confirm: string;
};
const SignUpPage = () => {
const { handleSubmit, control, reset } = useForm<TSignUpProps>({
defaultValues: {
email: "",
password: "",
confirm: "",
},
});
const onSubmit: SubmitHandler<TSignUpProps> = (data) => {
alert(JSON.stringify(data));
};
return (
<div>
<h3 className="text-center mb-5 text-primary-500">Create an Account</h3>
<form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-5">
<Input.Control<TSignUpProps>
name="email"
control={control}
rules={{ required: true }}
/>
<Input.Control<TSignUpProps>
name="password"
control={control}
rules={{ required: true }}
/>
<Input.Control<TSignUpProps>
name="confirm"
control={control}
rules={{ required: true }}
/>
<Button type="submit">Submit</Button>
</form>
</div>
);
};
export default SignUpPage;
- 잘 작동된다!