๐ react-hook-form๋ฅผ ์ด์ฉํ Input ์ปดํฌ๋ํธ
npm i react-hook-form @hookform/resolvers yup
react-hook-form
์ ์์ฝ๊ฒ form์ ์กฐ์ํ๊ณ ์ต์ ํํ ์ ์๋๋ก ๋์์ฃผ๋ ํจํค์ง์
๋๋ค.
๊ฑฐ๊ธฐ์ @hookform/resolvers
์ yup
์ ๋ํ๋ฉด ์์ฝ๊ฒ validation ์ฒ๋ฆฌ์ ์๋ฌ๋ฉ์ธ์ง๋ฅผ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
ํ์ฌ ๋๋ฃ๋ค์ด ์ด ์กฐํฉ์ผ๋ก ์ผ๋ฌด์ง๊ฒ ์ฌ์ฉํ๋ ๊ฒ์ ๋ณด๊ณ ์ ์ ๋ ์ฌ์ฉํ ๋๋ง ๊ธฐ๋ค๋ฆฌ๋ค, ๋ง์นจ ์๋ก์ด ํ๋ก์ ํธ๋ฅผ ์์ํ๋ ๊น์ ๋ฐ๋ก ๋์ ํ์ต๋๋ค.
๊ธฐํ์ ๋์ถฉ ๋ณด๋ input์ ์ค๋ณต๋๋ ์ฌ๋ฌ ๊ธฐ๋ฅ์ด ์์ด์ ๊ทธ๋ฅ ํ๋๋ก ๊ธฐ๋ฅ๋ค์ ๋ฌถ์ผ๋ฉด ์ข๊ฒ ๋ค ์ถ์ด ๋ค์๊ณผ ๋น์ทํ ํํ๋ก ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด๋ดค์ต๋๋ค.
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import { useForm } from "react-hook-form";
export default function App() {
const {
register,
handleSubmit,
formState: { errors }
} = useForm({
resolver: yupResolver(
yup.object().shape({ id: yup.string().required("์์ด๋๋ฅผ ์
๋ ฅํด์ฃผ์ธ์") })
)
});
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<InputComponent
placeholder="์์ด๋๋ฅผ ์
๋ ฅํด์ฃผ์ธ์"
error={errors.id?.message}
{...register("id")}
/>
<button type="submit">Submit</button>
</form>
);
}
type InputType = {
error?: string;
} & React.InputHTMLAttributes<HTMLInputElement>;
const InputComponent = ({ error, ...rest }: InputType) => {
return (
<div style={{ color: "red" }}>
<div>
<input {...rest} />
</div>
{error && error}
</div>
);
};
์๋ ์ ๊ฐ ๊ธฐ๋ฅ์ ์ผ๋ก๋ง ๋น์ทํ๊ฒ ๋ง๋ค์ด๋ณธ TextInput ์ปดํฌ๋ํธ์ ๋๋ค.
react-hook-form์ register๋ฅผ ์ด์ฉํด {...register("id")}
์ ๊ฐ์ด ์คํ๋ ๋ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ๋ฉด ์์ฒด์ ์ผ๋ก ๊ด๋ฆฌํ๋ ์์ฑ์ ์ ๋ถ ๋๊ฒจ์ค ์ ์์ด ์ ์๋ํ๊ฒ ๋ค๊ณ ์๊ฐํ์ต๋๋ค.
๊ทธ๋ฐ๋ฐ ์๋ฌด๋ฆฌ input์ ๊ฐ์ ๋ฃ๊ณ submit์ ํด๋ด๋ ๊ฐ์ด ์๋ค๊ณ validation์ ํด๋์ console ์ฐฝ์ ๋ณด๋ ๋ค์๊ณผ ๊ฐ์ warning์ด ์๋๋ฃฉํ๊ฒ ์ฐํ์์์ต๋๋ค.
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
๐ ๋น์ ์ด ์ปดํฌ๋ํธ
react-hook-form์ ๋ํ ๊ฐ๋ ์ ์ด๋ฏธ ์๊ณ ์์์ง๋ง ์ฒ์ ์ฌ์ฉํด ๋ด์ ๋ฐ์ํ ์๋ฌ์ ์ผํ๊ณ ์๋ ์ค ๋น์ ์ด ์ปดํฌ๋ํธ๋ผ๊ณ ํ๋ ๊ฒ์ด ๋ ์ฌ๋์ต๋๋ค.
์ฐ๋ฆฌ๊ฐ ํ์์ ์ํ์ onChange๋ฅผ ์ด์ฉํด ์ ๋ ฅ๋ง๋ค re-render๊ฐ ๋ฐ์ํ๋ ๊ฒ ์ ์ด ์ปดํฌ๋ํธ,
ref๋ฅผ ์ด์ฉํด DOM์ ์ง์ ์ ๊ทผํ ํ ๊ฐ์ ์กฐ์ํ๊ธฐ ๋๋ฌธ์ re-render๊ฐ ๋ฐ์ํ์ง ์๋ ๊ฒ์ด ๋น์ ์ด ์ปดํฌ๋ํธ์ ๋๋ค.
react-hook-form์ ์์ฒด์ ์ผ๋ก ref๋ฅผ ์ด์ฉํ ๋น์ ์ด ์ปดํฌ๋ํธ๊ธฐ ๋๋ฌธ์ ์์ฒด์ ์ผ๋ก input์ ์ ์ดํ๋ register("id")
๊ฐ์ฒด ๋ด๋ถ์ ref๊ฐ ์์ ํ๋ฅ ์ด 100%๋ผ๊ณ ์๊ฐํ์ต๋๋ค.
๊ทธ๋์ ํ์ ์ ๋ฐ๋ผ๊ฐ ๋ณด๋ ์ญ์๋ input ์์ฑ๊ณผ ํจ๊ป ref๊ฐ ์กด์ฌํ์ต๋๋ค.
๐ forwardRef()
ํจ์ํ ์ปดํฌ๋ํธ์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ref๋ฅผ ์์ ์ปดํฌ๋ํธ์๊ฒ props๋ก ๋๊ฒจ์ค ์ ์์ต๋๋ค.
ํ์ง๋ง forwardRef ํจ์๋ฅผ ์์ ์ปดํฌ๋ํธ์ ๊ฐ์ธ์ฃผ๋ฉด ref๋ฅผ ์ ๋ฌํ ์ ์์ผ๋ฉฐ, ์ด๋ฅผ ํตํด ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ์์ ์ปดํฌ๋ํธ์ DOM ์์์ ์ ๊ทผํ ์ ์์ต๋๋ค.
export const App = () => {
const ref = useRef<HTMLInputElement>(null);
return (
<>
<Children ref={ref} />
<button onClick={() => console.log(ref)}>Click</button>
</>
);
};
const Children = forwardRef<HTMLInputElement>((_, ref) => {
return <input ref={ref} />;
});
์์ ์ปดํฌ๋ํธ์ forwardRef ๊ฐ์์ฃผ๊ณ (props, ref) ์์๋ก ๋ค์ด์ค๋ ref๋ฅผ ์ํ๋ ์ปดํฌ๋ํธ์ ๋ฃ์ด์ฃผ๋ฉด ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ๋ง์๊ป ์ ์ด๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
๐ react-hook-form + forwardRef ํฉ์ฒด
๊ทธ๋ผ TextInput์์ forwardRef๋ฅผ ๊ฐ์์ฃผ๋ฉด react-hook-form์ด ref๋ฅผ ์ด์ฉํด ๋ง์๊ป ์กฐ์ํ๊ฒ ์ฃ ?
๊ทผ๋ฐ ๋๋ ref
๋ ๋ฃ์ด์ฃผ๊ณ ์ถ๊ณ , HTMLInputElement
๋ ๋ฃ์ด์ฃผ๊ณ ์ถ์๋ฐ ์ด๊ฑธ ์คํ๋ ๋ ์ฐ์ฐ์๋ก ํ ๋ฒ์ ๋ฟ๋ฆฌ๊ธฐ๋ ์ ๋งคํด register ๊ฐ์ฒด๋ฅผ ๊ทธ๋ฅ props๋ก ๋ฐ์ ๋ฐ๋ก๋ฐ๋ก ๋ฟ๋ ค์ฃผ๊ธฐ๋ก ํ์ต๋๋ค.
export default function App() {
// ...
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<InputComponent
placeholder="์์ด๋๋ฅผ ์
๋ ฅํด์ฃผ์ธ์"
error={errors.id?.message}
register={register("id")}
/>
<button type="submit">Submit</button>
</form>
);
}
type InputType = {
error?: string;
register?: UseFormRegisterReturn;
} & React.InputHTMLAttributes<HTMLInputElement>;
const InputComponent = ({ error, register, ...rest }: InputType) => {
return (
<div style={{ color: "red" }}>
<div>
<input {...rest} {...register} />
</div>
{error && error}
</div>
);
};
์๊น ์์์ ํ์ธํ๋ UseFormRegisterReturn
ํ์
์ ๊ฐ์ฒด๋ฅผ prop์ผ๋ก ๋ฐ์์ต๋๋ค.
prop์ด ์ค๋ณต๋ ๋ ๋์ค์ ๋ฟ๋ฆฐ ๋ฐ์ดํฐ๊ฐ ๋ฎ์ด์ฐ๋๊น …rest
๋ฅผ ๋จผ์ , ์ค์ํ ...register
๋ฅผ ๋์ค์ ๋ฟ๋ ค์ค๋๋ค.
์ง์ ํ์ธํด ๋ณด๋ฉด ref๊ฐ ์ ๋๋ก ์ ์ฉ๋์ด ์๋ํ๋ ๋ชจ์ต์ ๋ณผ ์ ์์ต๋๋ค.
๋๊ธ