[react] react-hook-form

누리·2023년 4월 11일
0

React Foundation

목록 보기
18/18

💡 react-hook-form의 필요성

간단한 회원가입 폼을 만들기 위해서 우리는 useState로 하나하나

  • state를 선언해주어야 하고
  • state 를 다루기 위해서 핸들링 함수를 만들어야 하고
  • 에러를 위한 state를 선언해주어야 하고
  • 검증을 위한 함수를 만들어야 한다
function NoHookForm() {
  const [name, setName] = useState("");
  const [id, setId] = useState("");
  const [pwd, setPwd] = useState("");
  const [phone, setPhone] = useState("");
  const [email, setEmail] = useState("");
  const [errors, setErrors] = useState({
    name: {
      invalid: false,
      message: "이름이 너무 깁니다.",
    },
    id: {
      invalid: false,
      message: "id는 3글자 이상, 20글자 이하여야 합니다.",
    },
    pwd: {
      invalid: false,
      message: "비밀번호는 10자 이하여야 합니다.",
    },
    phone: {
      invalid: false,
      message: "전화번호 형식에 맞지 않습니다.",
    },
    email: {
      invalid: false,
      message: "이메일 형식에 맞지 않습니다.",
    },
  });

  const handleName = (event: React.ChangeEvent<HTMLInputElement>) => {
    setName(event.currentTarget.value);
  };
  const handleId = (event: React.ChangeEvent<HTMLInputElement>) => {
    setId(event.currentTarget.value);
  };
  const handlePwd = (event: React.ChangeEvent<HTMLInputElement>) => {
    setPwd(event.currentTarget.value);
  };
  const handlePhone = (event: React.ChangeEvent<HTMLInputElement>) => {
    setPhone(event.currentTarget.value);
  };
  const handleEmail = (event: React.ChangeEvent<HTMLInputElement>) => {
    setEmail(event.currentTarget.value);
  };

  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    if (name.length > 10) {
      setErrors((prev) => ({
        ...prev,
        name: {
          ...prev.name,
          invalid: true,
        },
      }));
    }
    if (id.length < 3 || id.length > 20) {
      setErrors((prev) => ({
        ...prev,
        id: {
          ...prev.id,
          invalid: true,
        },
      }));
    }
    // ...등.. 에러 처리 이후 로직 필요.
  };

  return (
    <form onSubmit={handleSubmit}>
      <input value={name} onChange={handleName} />
      {errors.name.invalid ? (
        <p className="error">{errors.name.message}</p>
        ) : null}
      <input type="text" value={id} onChange={handleId} />
      {errors.id.invalid ? (
        <p className="error">{errors.id.message}</p>
      ) : null}
      <input type="text" value={pwd} onChange={handlePwd} />
      <input type="text" value={phone} onChange={handlePhone} />
      <button>제출</button>
    </form>
  );
}

export default NoHookForm;

코드의 길이도 길지만, React 에서 컴포넌트 리랜더링이 발생하는 조건 중 하나는 state 의 값이 변했을 때다. 위의 코드에서는 모든 값이 state 로 연결되어 있다. 그렇게되면 하나의 값이 변할때 마다 여러개의 자식 컴포넌트 들에서 무수히 많은 리랜더링이 발생하게되는데 이는 개발자가 예측한 랜더링이 아닌, 불필요한 랜더링으로서 불필요한 연산이 일어나 퍼포먼스를 약화시킬 수 있다.

react-hook-form 사용법

npm i react-hook-form

1. useForm 훅을 불러와 register함수를 사용한다.

이 때 내가 사용할 모든 inputregister함수를 호출해서 사용 해야한다.

register("해당필드를다룰키값", 옵션객체)

첫번째의 키값은 필수요소이고 옵션으로 들어갈 값으로는 required, min, max, minLength, maxLength, pattern 등이 있다.
유효성 검사를 위해 옵션: value 만을 줄수도 있지만, 옵션: 에러message 로 구성된 객체를 줌으로써 해당 에러에 대한 구체적인 메세지를 제공할 수도 있다.

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

interface IForm {
  name: string;
  id: string;
  pwd: string;
  pwd1: string;
  phone: string;
}

const useHook = () => {
  const { register } = useForm<IForm>({
    defaultValues : {
      id: "@naver.com"
    }
  });
  return (
    <form>
      <input {...register("name", {required: true })} />
      <input {...register("id", {required: "id를 입력해주십시오" })} />
      <input {...register("pwd", {
        required: true, 
        minLength: 5 })} />
	  <input {...register("pwd1", {
        required: true, 
        minLength: 5 })} />
      <input {...register("phone", {
        required: true, 
        minLength: {
          value: 10, 
          message: "번호를 10자리 이상 입력해주세요"
        }})} />/>
      <button>제출</button>
    </form>
  );
}
export default useHook;

register 함수가 반환하는 객체를 input에 props로 제공한다.

2. formState내부의 errors 객체를 찾는다.

formState에는 form의 여러가지 state가 들어있고 우리는 특히 error에 접근하여 정보를 얻어야한다.
그리고 errors에 접근하면 내가 register로 생성한 키값으로 각각의 에러에 접근할 수 있다.

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

interface IForm {
  name: string;
  id: string;
  pwd: string;
  pwd1: string;
  phone: string;
}

const useHook = () => {
  const { register, formState: {errors} } = useForm<IForm>({
    defaultValues : {
      id: "@naver.com"
    }
  });
  
  return (
    <form>
      <input {...register("name", {required: true })} />
	  <span>{errors?.name?.message}</span>
      <input {...register("id", {required: "id를 입력해주십시오" })} />
	  <span>{errors?.id?.message}</span>
      <input {...register("pwd", {
        required: true, 
        minLength: 5 })} />
	  <span>{errors?.pwd?.message}</span>
      <input {...register("pwd1", {
        required: true, 
        minLength: 5 })} />
	  <span>{errors?.pwd1?.message}</span>
      <input {...register("phone", {
        required: true, 
        minLength: {
          value: 10, 
          message: "번호를 10자리 이상 입력해주세요"
        }})} />/>
      <span>{errors?.phone?.message}</span>
      <button>제출</button>
    </form>
  );
}
export default useHook;

3. handleSubmit 함수를 호출해 모든 validation을 수행한다.

handleSubmit은 useForm 훅에서 제공하는 함수로 onValid 함수(내가생성한 함수)를 하나의 인자로 갖는다.
폼에서 데이터를 입력하고 제출시에 submit 이벤트가 발생하게 되는데, 우리는 서버에 데이터를 넘기기 전에 해당 데이터에 대한 검증을 끝낼 필요가 있다. 그러기 위해서 form 태그의 onSubmit 에 handleSubmit 이라는 함수를 넣어주고 매개변수로 우리가 정의한 onSubmit 함수를 넣어준다. onSubmit 함수를 정의 할 때 매개변수로 data 값을 받을 수 있는데, 해당 값은 사용자가 제출 버튼을 클릭 한 후 내려오는 사용자 입장에서 최종으로 제출하는 데이터 이다.

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

interface IForm {
  name: string;
  id: string;
  pwd: string;
  pwd1: string;
  phone: string;
}

const useHook = () => {
  const { register, 
         handleSubmit, 
         formState: { errors }, 
         setError, 
  } = useForm<IForm>({
    defaultValues : {
      id: "@naver.com"
    },
  });
  
  const onValid = (data: IForm) => {
    if(data.pwd !== data.pwd1){
      return setError(
        "pwd1",
        {message: "Passwords are not the same"}, 
        {shouldFocus: true}
      );
    }
  }
  return (
    <form onSubmit={handleSubmit(onValid)}>
      <input {...register("name", {required: true })} />
	  <span>{errors?.name?.message}</span>
      <input {...register("id", {required: "id를 입력해주십시오" })} />
	  <span>{errors?.id?.message}</span>
      <input {...register("pwd", {
        required: true, 
        minLength: 5 })} />
	  <span>{errors?.pwd?.message}</span>
      <input {...register("pwd1", {
        required: true, 
        minLength: 5 })} />
	  <span>{errors?.pwd1?.message}</span>
      <input {...register("phone", {
        required: true, 
        minLength: {
          value: 10, 
          message: "번호를 10자리 이상 입력해주세요"
        }})} />/>
      <span>{errors?.phone?.message}</span>
      <button>제출</button>
    </form>
  );
}
export default useHook;

특정 조건의 유효성 검사를 할때 onValid 함수를 생성한다.
onSubmit 에서 최종 데이터 검증을 하다가 어떤 조건에 맞지 않을때 setError 함수를 활용하여 에러를 발생시킬 수 있다. 그리고 setFocus 를 활용해 해당 필드에 포커스도 줄 수 있다.

4. setValue를 사용해 input 폼을 리셋한다.

위의 onValid 함수에서 data값이 유효성 검사를 통과하면 다시 input 폼을 비우는 함수로 setValue("input키값", "설정할값")를 사용한다.

profile
프론트엔드 개발자

0개의 댓글