TECH

React Hook Form์˜ ์„œ๋กœ ๋น„์Šทํ•œ ๊ธฐ๋Šฅ๋“ค์„ ๋น„๊ตํ•ด ๋ณด์ž

ttaerrim 2024. 2. 3. 15:46

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” React Hook Form์„ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๋А๊ผˆ๋˜ ๋น„์Šทํ•œ ๊ธฐ๋Šฅ์„ ํ•˜๋Š” API๋“ค์„ ๋น„๊ตํ•˜๊ณ  ์‚ฌ์šฉํ•˜๋ฉฐ ๋А๋‚€ ์ ๋“ค์„ ์ •๋ฆฌํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

 

 

 

 

1๏ธโƒฃ Controller vs. useController

AntD, MUI์™€ ๊ฐ™์€ UI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‚˜ ์ปค์Šคํ…€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ Controller๋‚˜ useController๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ React Hook Form์˜ ๊ธฐ๋Šฅ์„ ์‰ฝ๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
 

 

Controller

function Component() {
    const { control } = useForm<{type: string}>();
    
    return (
      <Controller
          control={control}
          name='type'
          render={({field : {onChange, value}}) => (
          <input
            type='input'
            value={value}
            onChange={onChange}
      	  />
         )}
      />
    )
}

 

Controller๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๋А๊ผˆ๋˜ ์ ์€, Controller์˜ render ์†์„ฑ์˜ return ๋ฌธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ์†์„ฑ์ด ๋งŽ์•„์งˆ์ˆ˜๋ก, ๋˜๋Š” ํ•œ ์ปดํฌ๋„ŒํŠธ์— ์‚ฌ์šฉ๋˜๋Š” Controller๊ฐ€ ๋งŽ์•„์งˆ์ˆ˜๋ก ์ฝ”๋“œ ๋ผ์ธ๊ณผ ๋“ค์—ฌ์“ฐ๊ธฐ ํšŸ์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ•ด ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง„๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. 

Controller์™€ ๋™์ผํ•œ ๊ธฐ๋Šฅ์„ ํ•˜๋Š” useController ํ›…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 


useController

function Component() {
    const { control } = useForm<{type: string}>()
    const { field } = useController({ name: 'type', control })
    
    return (
        <input
          type='input'
          value={field.value}
          onChange={field.onChange}
        />
    )
}

 

 




2๏ธโƒฃ setValue vs. reset

React Hook Form - useForm: reset์— ๋”ฐ๋ฅด๋ฉด reset์€ ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์„ ํ•  ๋•Œ ์šฉ์ดํ•˜๊ณ , setValue๋Š” ์†Œ๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•  ๋•Œ ์šฉ์ดํ•ฉ๋‹ˆ๋‹ค.
์ €๋Š” ๋ชจ๋“  ํผ์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒฝ์šฐ๋Š” reset์„, ํŠน์ • ํ•„๋“œ๋งŒ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋Š” setValue๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

import { useForm } from 'react-hook-form';  

export function Component() {  
  const { setValue, reset } =  
    useForm();  
    
  // ์ƒ๋žต  

  useEffect(() => {  
    if (data) {  
      const {  
        title,  
        groupId,  
        address,  
        latitude,  
        longitude,  
        contents,  
        maxHumanCount,  
        date,  
        status,  
      } = data;  

      reset({  
          title,  
          groupId,  
          address,  
          latitude,  
          longitude,  
          contents,  
          date,  
          maxHumanCount,  
          status,  
        });  
    }  
  }, [data, reset]);  
  
  const setGroup = (groupId: string) => {  
      setValue('groupId', groupId);  
  }  
  // ์ƒ๋žต...  
}

 

 




3๏ธโƒฃ rules vs. schema

Controller๋‚˜ useController๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, rules ์†์„ฑ์„ ์ง€์ •ํ•˜์—ฌ ํ•„๋“œ์˜ ๊ทœ์น™์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

rules

import { useController, useForm } from "react-hook-form";

export default function App() {
  const { control } = useForm({ mode: "all" });
  const {
    field,
    fieldState: { error },
  } = useController({
    name: "text",
    control,
    rules: { required: "Text is required" },
  });

  return (
    <>
      <input onChange={field.onChange} value={field.value} />
      {error && <p>{error?.message}</p>}
    </>
  );
}

 



schema

schema๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•ด์„œ ์‚ฌ์šฉํ•  ๋•Œ๋Š” yup, zod, joi์™€ ๊ฐ™์€ schema validation ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { useController, useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";

export default function App() {
  const schema = yup.object().shape({
    text: yup.string().required("Text is required"),
  });

  const { control } = useForm({ mode: "all", resolver: yupResolver(schema) });
  const {
    field,
    fieldState: { error },
  } = useController({
    name: "text",
    control,
  });

  return (
    <>
      <input onChange={field.onChange} value={field.value} />
      {error && <p>{error?.message}</p>}
    </>
  );
}



 

๋‹ค์Œ๊ณผ ๊ฐ™์ด yup์˜ test ๋ฉ”์†Œ๋“œ๋กœ ์ปค์Šคํ…€ ์—๋Ÿฌ ํƒ€์ž…์™€ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฃผ๋กœ API ์š”์ฒญ์„ ํ†ตํ•ด ๋‹‰๋„ค์ž„ ์ค‘๋ณต ํ™•์ธ๊ณผ ๊ฐ™์€ ์ ˆ์ฐจ๋ฅผ ๊ฑฐ์น˜๊ณ  ์‹ถ์„ ๋•Œ schema validation์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

import { useController, useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";

export default function App() {
  const duplicated = ["a", "b", "c", "aa", "aaa", "bb", "bbb", "cc", "ccc"];
  const schema = yup.object().shape({
    text: yup
      .string()
      .required("Text is required")
      .test(
        "Unique",
        "Values need te be unique",
        (values) => !duplicated.includes(values)
      ),
  });

  const { control } = useForm({ mode: "all", resolver: yupResolver(schema) });
  const {
    field,
    fieldState: { error },
  } = useController({
    name: "text",
    control,
  });

  return (
    <>
      <input onChange={field.onChange} value={field.value} />
      {error && <p>{error?.message}</p>}
    </>
  );
}