ํ๋ฉ ๊ฐ์ค ํ์ด์ง๋ฅผ ๊ตฌํํ๋ ๋์ค ์
๋ ฅ ํ๋๊ฐ ๋ง์์ ธ ์ํ๊ด๋ฆฌ์ ํ์์ฑ์ ๋๊ผ๊ณ , ๋ฌด์๋ณด๋ค gift item list์ dnd-kit(๋๋๊ทธ์ค๋๋กญ)๋ฅผ ๋ถ์๋๋ ๋์ฐํ ๋ฆฌ๋ ๋๋ง ์ด์๊ฐ ๋ฐ์ํ๋ค.
02์ธ๋์ ์๊ฐํด๋ณธ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ด ๋ ๊ฐ์ง๋ค.
์ฌ์ค ๋ ๋ฐฉ๋ฒ ๋ชจ๋ ์ฌ์ฉํด๋ณด์ง ์์์ ๋ญ๋ ์ข์ ๊ฒ ๊ฐ์๋ค. ์ธ๋๊ฐ ํด๋ณด๊ณ ์ถ์ ๊ฑธ ์ ํํด๋ ์ข์ ๊ฒ ๊ฐ๋ค๋ ์ฒจ์ธ์ ํด์ค์ React Hook Form์ ํ์ฉํด ํจ๊ณผ์ ์ผ๋ก ํผ ๊ด๋ฆฌ๋ฅผ ํด๋ณด๊ธฐ๋ก ๊ฒฐ์ ํ๋ค.
(๊ฐ์์ ์ฅ์ ์ด ๋ถ๋ช
ํ ์์ํ
๋ฐ.. ์๊ฐ๋ด์ ์ด ๋ถ๋ถ๋ ์ ๋ฆฌํด๋๊ฒ ์ต๋๋ค._)
๋ฆฌ์กํธ์์ ์ฌ์ฉ์ ์
๋ ฅ๊ฐ์ ๊ด๋ฆฌํ๊ธฐ ์ํด์ useState๋ useRef๋ฅผ ์ด์ฉํ ์ ์์ง๋ง, React Hook Form๋ฅผ ์ฌ์ฉํ๋ฉด ๋ถํ์ํ ๋ ๋๋ง์ ์ค์ด๋ฉด์ ๋ ๊ฐ๋จํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค.
React Hook Form์ ref ๊ธฐ๋ฐ์ด๋ฏ๋ก, value ์์ฑ์ด ํ์ํ์ง ์๋ค. (defaultValue๋ฅผ ์ฌ์ฉํ์!)
๋ฐ๋ก API๋ก ๋์ด๊ฐ๋ณด์.
useForm์ React-Hook-Form์์ ๊ฐ์ฅ ์ฃผ๊ฐ ๋๋ ์์์ด๋ฉฐ, ํผ์ ์ฝ๊ฒ ๊ด๋ฆฌํ๊ธฐ ์ํ ์ปค์คํ ํ ์ด๋ค.
FormProvider๋ React-Hook-Form์์ ์ ๊ณตํ๋ ์ปดํฌ๋ํธ๋ก, React์ Context API๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ตฌํ๋์๋ค. FormProvider๋ ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ์์ ์ปดํฌ๋ํธ๋ก ํผ ๋ฐ์ดํฐ์ ๊ด๋ จ๋ ์ํ+๋ก์ง์ ์ ๋ฌํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ค.
useFormContext๋ React-Hook-Form์ ์ปจํ ์คํธ์ ์ ๊ทผํ๊ธฐ ์ํ ์ปค์คํ ํ ์ด๋ค. useFormContext๋ฅผ ์ฌ์ฉํ๋ฉด ์ปจํ ์คํธ๋ฅผ Prop์ผ๋ก ์ ๋ฌํ์ง ์๊ณ ์ ๊ทผํ ์ ์๊ธฐ์ nested structures์์ ๋งค์ฐ ์ข๋ค. ์ด ํ ์ ์ฌ์ฉํ๋ฉด useForm์์ ๋ฐํํ๋ ๋ชจ๋ ๋ฉ์๋์ ์์ฑ์ ๊ฐ์ ธ์ฌ ์ ์๋ค. ์ฆ, useForm์ ๋ฐํ๊ฐ์ ๊ทธ๋๋ก ์ฌ์ฉํ ์ ์๋ค.
const { methods } = useForm();
/* FormProvider ์ props๋ก useForm ํ
์ ์ฌ์ฉํ์ฌ ๊ฐ์ ธ์จ ํผ ๋ฉ์๋(methods)๋ฅผ ๋๊ฒจ์ค */
<FormProvider {...methods} >
/* ํผ ์ปดํฌ๋ํธ๋ฅผ ํฌํจํ ์ปดํฌ๋ํธ๋ฅผ ์์ ์ปดํฌ๋ํธ๋ก ์ฌ์ฉํ๋ฉด props๋ฅผ ๋๊ฒจ์ผ ํ์ง๋ง,
FormProvider๋ก ๊ฐ์ผ๋ค๋ฉด ๋ฐ๋ก props๋ฅผ ๋๊ธฐ์ง ์์๋ ๋๋ค. */
<์์ ์ปดํฌ๋ํธ />
...
useFormContext๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ form์ FormProvider ์ปดํฌ๋ํธ๋ก ๊ฐ์ธ์ค์ผ ํ๋ค. FormProvider ์ปดํฌ๋ํธ์ useForm์์ ๋ฐํํ ๋ฉ์๋์ ์์ฑ(methods)์ ์ ๋ฌํ๋ฉด ๋๋ค.
๊ทธ ์ดํ, ์์ ์ปดํฌ๋ํธ์์ useFormContext๋ฅผ ํธ์ถํ์ฌ ํด๋น ๋ฉ์๋์ ์์ฑ์ ๊ฐ์ ธ์ ์ฌ์ฉํ ์ ์๋ค.
// ์์ ์ปดํฌ๋ํธ
const { register } = useFormContext();
...
return <TextField label="์ ๋ชฉ" fullWidth {...register("fundTitle")} />
์ฌ๊ธฐ์ ์ค์ํ ๊ฑด โญprops drillingโญ ์์ด ์ฌ์ฉ ๊ฐ๋ฅํ๋ค๋ ์ !์ด๋ค.
<input
type="text"
{...register("email", {
pattern: {
value:
/^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i,
message: "์ฌ๋ฐ๋ฅธ ์ด๋ฉ์ผ ํ์์ด ์๋์์ ๐",
},
})}
์๋๋ register ํจ์์์ ์ฌ์ฉํ ์ ์๋ ์ต์ ์ด๋ค.
register: (name: string, RegisterOptions?) => ({ onChange, onBlur, name, ref });<br>
type RegisterOptions = Partial<{
required: boolean | string | { value: boolean; message: string; }; // ํ์๊ฐ ์ฌ๋ถ ์ฒดํฌํฉ๋๋ค.
min: number | string | { value: number | string; message: string }; // ์
๋ ฅ ๊ฐ์ ์ต์๊ฐ ์ฒดํฌํฉ๋๋ค.
max: number | string | { value: number | string; message: string }; // ์
๋ ฅ ๊ฐ์ ์ต๋๊ฐ ์ฒดํฌํฉ๋๋ค.
maxLength: number | string | { value: number | string; message: string }; // ์
๋ ฅ ๊ฐ์ ์ต๋ ๊ธธ์ด ์ฒดํฌํฉ๋๋ค.
minLength: number | string | { value: number | string; message: string }; // ์
๋ ฅ ๊ฐ์ ์ต์ ๊ธธ์ด ์ฒดํฌํฉ๋๋ค.
pattern: RegExp | { value: RegExp; message: string }; // ์ ๊ท์์ ์ฌ์ฉํ ์
๋ ฅ ๊ฐ ์ฒดํฌํฉ๋๋ค.
validate: Validate | Record<string, Validate>; // ์ปค์คํ
์ ํจ์ฑ ์ฒดํฌ๋ฅผ ๋ง๋ค ๋ ์ฌ์ฉ๋ฉ๋๋ค.
valueAsNumber: boolean // Number๋ก ํ๋ณํ ํฉ๋๋ค.
valueAsDate: boolean // Date๋ก ํ๋ณํ ํฉ๋๋ค.
setValueAs: (value: any) => any // value๋ฅผ ๊ฐ๊ณตํฉ๋๋ค.
disabled: boolean // true๋ก ์ค์ ์, value๊ฐ undefined ๋๋ฉฐ ์์๊ฐ disabled ๋ฉ๋๋ค.
onChange: (event: any) => void // change ์ด๋ฒคํธ ๋ฐ์ ์ ์คํ๋๋ ์ฝ๋ฐฑ์
๋๋ค.
onBlur: (event: any) => void // blur ์ด๋ฒคํธ ๋ฐ์ ์ ์คํ๋๋ ์ฝ๋ฐฑ์
๋๋ค.
value: unknown // ์์๊ฐ ๊ฐ์ง๊ณ ์๋ ๊ฐ์
๋๋ค.
shouldUnregister: boolean // true๋ก ์ง์ ํ ๊ฒฝ์ฐ ์์๊ฐ ์ธ๋ง์ดํธ ๋๋ฉด ์
๋ ฅ๊ฐ์ด ์ ๊ฑฐ๋ฉ๋๋ค.
deps: string | string[] // ๋์ด๋ ์์๊ฐ ํจ๊ป ์ ํจ์ฑ ๊ฒ์ฆ์ด ์คํ๋ฉ๋๋ค.
}>;
type Validate<TFieldValue, TFormValues> = (value: TFieldValue, formValues: TFormValues) => ValidateResult | Promise<ValidateResult>;
type ValidateResult = Message | Message[] | boolean | undefined;




์ ์
๋ ฅํผ๋ค์ ๊ฐ๊ฐ์ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌํ ์์ ์ด๋ค.
์ด์ ๋.. FormProvider์ useFormContext๋ฅผ ์ฌ์ฉํ๋ ํต์ฌ ๊ฐ๋
์ธ ๋ก์ปฌ ํผ ์ํ๋ฅผ ํ์ฉํ์ฌ ์ต์ ํํ๊ธฐ ์ํด์๋ค.
๋ก์ปฌ ํผ ์ํ๋ ํด๋น ์ปดํฌ๋ํธ๊ฐ ์ง์ ์ ์ผ๋ก ์์กดํ๋ ํผ ํ๋์ ๋ํด์๋ง ๊ด๋ฆฌํ๊ณ ์
๋ฐ์ดํธํ๋ฏ๋ก, ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ๋ฆฌ๋ ๋๋ง์ ์ํฅ์ ๋ฐ์ง ์๋๋ค. ๋ฐ๋ผ์ ๊ฐ ์ปดํฌ๋ํธ์ ์ฑ๋ฅ์ด์๋ ํด๋น ํ๋์๋ง ์ํฅ์ ๋ฐ๊ณ , ๋ค๋ฅธ ๋ถ๋ชจ๋ ํ์ ์ปดํฌ๋ํธ๋ค์ ๋ฆฌ๋ ๋๋ง๊ณผ๋ ๋
๋ฆฝ์ ์ด๋ค.
๐จ ๋ฒ deeply nested structures ๋ผ๋ฉด?
๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ FormProvider๋ก ๊ฐ์ธ์ ธ์๊ณ , ๊ทธ ํ์์ ์ฌ๋ฌ ๊ฐ์ ์์ ์ปดํฌ๋ํธ๊ฐ ์๋ค๊ณ ํด๋ณด์.
๊ทธ ์์ ์ปดํฌ๋ํธ๋ค ์ค์์ useFormContext๋ฅผ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ๊ฐ ์๋ค๋ฉด, ํด๋น useFormContext๋ฅผ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ์ ๋ก์ปฌ ํผ ์ํ ๋ณ๊ฒฝ ์, ๊ทธ ์ปดํฌ๋ํธ์ ๊ทธ์ ์์ ์ปดํฌ๋ํธ๋ค์ด ๋ฆฌ๋ ๋๋ง ๋ ์ ์๋ค.
์ด๋ React์ ์ปดํฌ๋ํธ ํธ๋ฆฌ์์ ์์๋ก์ ๋ฆฌ๋ ๋๋ง ์ ํ๋ก ์ธํด ๋ฐ์ํ๋ค.
๊ณ ๋ก... ๊น์ ๊ณ์ธต๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๋ ์ปดํฌ๋ํธ์์๋ useFormContext๋ฅผ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ์ ์ฑ๋ฅ์ ์ํฅ์ ์ฃผ๋ ์์๋ค์ ์ต์ ํํ๋ ๊ฒ์ด ์ค์ํ๋ค. ํ์ํ ๊ฒฝ์ฐ React.memo๋ shouldComponentUpdate๋ฅผ ์ฌ์ฉํ์ฌ ๋ถ๋ชจ ์ปดํฌ๋ํธ์ ๋ฆฌ๋ ๋๋ง์ ์ต์ ํํ๊ณ , useFormContext๋ฅผ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ ์์ฒด์ ์ฑ๋ฅ์ ๊ฐ์ ํ ์ ์๋ค.
๐ก ๊ฒฐ๋ก ! Context๋ ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ์๋, ์์ ์ปดํฌ๋ํธ๋ค์ด ๋ฆฌ๋ ๋๋ง ๋ ์ ์์ผ๋ ์ด ๋ถ๋ถ์ ์ฃผ์ํด์ ์ฌ์ฉํ์.