[React] react-hook-form 유효성 체크 (VS. Formik)

nemo·2022년 6월 16일
0

React

목록 보기
22/28

react-hook-form VS. Formik

1. 성능 이슈

2. 리렌더링

  • Formik은 provider 방식이다. form 내에 있는 컴포넌트가 상태를 공유하므로 불필요한 리렌더링이 발생할 수 있다. (provider로 묶여 있으므로 특정 state만 watch할 수 없음)

  • react-hook-form은 provider 방식 대신 비제어 컴포넌트(ref) 방식을 사용해 불필요한 리렌더링을 방지할 수 있다.

3. 간결한 코드

  • Formik은 유효성 체크를 위해 보통 Yup 라이브러리를 함께 사용하며, 복잡한 Form을 다루기 어렵다.

  • react-hook-form은 비교적 코드량과 용량이 적으며, 리액트 syntax와 비슷하다.

4. 타입스크립트

  • react-hook-form은 타입스크립트를 지원한다.

react-hook-form 사용하기

0. 설치

npm install react-hook-form


1. 기본 form 세팅 & useForm 가져오기

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

...

const App = () => {
  return (
    <form>
      <label>Email</label>
      <input type="email" />
      <label>Name</label>
      <input type="text" />
      <label>Password</label>
      <input type="password" />
      <label>Password Confirm</label>
      <input type="password" />
      <input type="submit" />
    </form>
  );
}

2. register 등록

const App = () => {
  const { register, watch } = useForm();
  
  console.log(watch("email")); // watch

  return (
    <form>
      <label>Email</label>
      <input type="email" {...register("email")} />
      <label>Name</label>
      <input type="text" {...register("name")}/>
      <label>Password</label>
      <input type="password" {...register("password")}/>
      <label>Password Confirm</label>
      <input type="password" {...register("password_confirm")}/>
      <input type="submit" />
    </form>
  );
}

관찰하려는 input에 register를 등록하면 input 변화를 감지할 수 있다. 또한 watch를 통해 시각적으로 확인할 수 있다.

register는 실제 DOM에서 확인해보면 name으로 등록된다.


3. 필수 입력과 에러 메시지 세팅

  • 필수 입력 조건인 경우
<input type="password" {...register("password", {required : true })}/>
  • 필수 입력 조건에 형식이 있는 경우
<input type="email" {...register("email", {
    required: true,
    pattern: 정규식패턴입력
  })} />
  • 최대, 최소 길이
<input type="text" {...register("name", { required : true, maxLength: 10 })}/>

<input type="password" {...register("password", { required : true, minLength: 6 })} />

4. 에러 메시지 등록

에러 메시지는 유효성 조건에 따라 여러 개 등록할 수 있다.

필수 입력 조건 + 정해진 형식이 있는 경우

  • 에러 메시지 1: 이메일을 입력해주세요
  • 에러 메시지 2: 이메일 형식에 맞게 입력해주세요

우선 formState: { errors }를 가져온다

const { register, watch, formState: { errors } } = useForm<formData>();

&& errors.email.type 조건을 하나 더 추가하면 각 조건의 에러 메시지를 등록할 수 있다.

<input type="email" {...register("email", {
    required: true,
    pattern: {
      value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
      message: "이메일을 형식에 맞게 입력해주세요."
    }
  })} />
// 에러 메시지 1
{errors.email && errors.email.type === "required" && <span>이메일을 입력해주세요.</span>}
// 에러 메시지 2
{errors.email && errors.email.type === "pattern" && errors.email.message}

5. SubmitHandler 등록

const { register, watch, handleSubmit, formState: { errors } } = useForm<formData>();

// data : input에 입력한 data가 매개변수로 들어온다
const onSubmit: SubmitHandler<formData> = (data) => {
  console.log(data);
}
const onError = (error) => {
  console.log(error);
};


return (
  <form onSubmit={handleSubmit(onSubmit, onError)}>
    ...
  </form>
);

handlesubmit은 submit 시 새로고침되지 않기 때문에 e.preventDefault()를 설정하지 않아도 된다.
handlesubmit의 두 매개변수가 제출된 값이 유효한지 검사해준다. 유효하다면 onSubmit이 실행되고 유효하지 않다면 onError가 실행된다.
enter로 submit하면 유효성 검사가 되지 않고 submit되는 이슈가 있는데, handlesubmit이 알아서 처리해주는 것이다.


⭐️ Confirm password 유효성 체크

watch를 통해 감지된 password와 일치한지 확인 후, 일치하지 않으면 에러 메시지를 출력한다.

<label>Password Confirm</label>
<input type="password" {...register("password_confirm", { 
    required : true,
    validate: (value: string) => {
      if (watch('password') !== value) {
        return "비밀번호가 일치하지 않습니다.";
      }
    },
  })}/>
{errors.password_confirm && errors.password_confirm.type === "required" && <p>비밀번호를 입력해주세요.</p>}
{errors.password_confirm && errors.password_confirm.type === "validate" && <p>{errors.password_confirm.message}</p>}

전체 코드

📎 Demo

import { useForm, SubmitHandler } from "react-hook-form";
import "./styles.css";

type formData = {
  email: string;
  name: string;
  password: string;
  password_confirm: string;
};

const App = () => {
  const { register, watch, handleSubmit, formState: { errors } } = useForm<formData>();

  // data : input에 입력한 data가 매개변수로 들어온다
  const onSubmit: SubmitHandler<formData> = (data) => {
    console.log(data);
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>Email</label>
      <input type="email" {...register("email", {
          required: true,
          pattern: {
            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
            message: "이메일을 형식에 맞게 입력해주세요."
          }
        })} />
      {errors.email && errors.email.type === "required" && <p>이메일을 입력해주세요.</p>}
      {errors.email && errors.email.type === "pattern" && errors.email.message}

      <label>Name</label>
      <input type="text" {...register("name", { required : true, maxLength: 10 })}/>
      {errors.name && errors.name.type === "required" && <p>이름을 입력해주세요.</p>}
      {errors.name && errors.name.type === "maxLength" && <p>10자 이하로 입력해주세요.</p>}

      <label>Password</label>
      <input type="password" {...register("password", { required : true, minLength: 6 })} />
      {errors.password && errors.password.type === "required" && <p>비밀번호를 입력해주세요.</p>}
      {errors.password && errors.password.type === "minLength" && <p>6자 이상 입력해주세요.</p>}

      <label>Password Confirm</label>
      <input type="password" {...register("password_confirm", { 
          required : true,
          validate: (value: string) => {
            if (watch('password') !== value) {
              return "비밀번호가 일치하지 않습니다.";
            }
          },
        })}/>
      {errors.password_confirm && errors.password_confirm.type === "required" && <p>비밀번호를 입력해주세요.</p>}
      {errors.password_confirm && errors.password_confirm.type === "validate" && <p>{errors.password_confirm.message}</p>}

      <input type="submit" />
    </form>
  );
}

export default App;

0개의 댓글