프론트엔드

[React] React Hook Form의 useForm 사용기

박수련 2024. 4. 11. 22:44

React hook form은 리액트에서 폼의 유효성 검사를 위한 라이브러리이다.

useForm을 가지고 간단한 폼을 만들어봤다. 

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

export default function ReactHookForm() {
  const [name, setName] = useState("");
  const {
    watch,
    register,
    formState: { errors, isSubmitSuccessful },
    handleSubmit,
    setValue,
    reset,
  } = useForm({ mode: "onBlur" });

  const handleChange = (e) => {
    setName(e.target.value);
  };

  console.log(watch());
  console.log(watch("username"));

  const onValid = () => {
    console.log("submitted");
    reset({
      username: "",
      email: "@naver.com",
    });
  };

  const onInvalid = (errors) => {
    console.log("errors", errors);
  };

  return (
    // 아래 블록
  );
}
<form onSubmit={handleSubmit(onValid,onInvalid)}>
  <input
    {...register("username", {
      required: "Username is required",
      minLength: {
        message: "The username should be longer than 5 chars",
        value: 5,
      },
    })}
    type="text"
    placeholder="Username"
    onChange={handleChange}
  />
  <span>{errors.username?.message}</span>

  <input
    id="email"
    type="email"
    {...register("email", {
      required: "Please write down your naver address",
      validate: {
        onlyNaver: (value) =>
          value.includes("@naver.com") || "Only naver address allowed",
      },
    })}
    placeholder="naver address"
  />
  <span>{errors.email?.message}</span>

  <input
    type="button"
    onClick={() => {
      setValue("email", `${name}@naver.com`);
    }}
    value="Set email with username"
  />

  <input type="submit" />
  {isSubmitSuccessful && (
    <div>
      <h1>{name}, Your form is successfully submitted!</h1>
    </div>
  )}
</form>

 

 

register

connects inputs and state, and apply validation

const { onChange, onBlur, name, ref } = register('firstName'); 
// include type check against field path with the name you have supplied.
        
<input 
  onChange={onChange} // assign onChange event 
  onBlur={onBlur} // assign onBlur event
  name={name} // assign name prop
  ref={ref} // assign ref prop
/>

// same as above
<input {...register('firstName')} />

 

 

 

required / minLength

required, minLength를 에러 메세지와 함께 설정할 수 있다.

 

validate

validate를 통해 원하는 유효성 검사를 할 수 있다. (callback 함수들의 객체로 구성)

 form에 console.log(errors.email) 를 찍어보면 다음과 같은 메세지를 확인할 수 있다. 

 

formState

formState 객체는 전체 form의 상태에 대한 정보를 담고 있다. 

const { formState: { errors, isSubmitSuccessful, isSubmitting, isDirty, touchedFields, submitCount } } = useForm<LoginForm>();
  • isSubmitting
  • isSubmitted: set to true after the form is submitted until the reset method is valid
  • isDirty: set to true when any of the input value is modified compared to defaultValues
  • dirtyFields
  • touchedFields
  • validationFields: fields which are getting async validation

 

watch

watch specified inputs and return their values

input에 입력된 값을 반환한다. 

const { watch } = useForm();

console.log(watch());
console.log(watch('username'));

 

handleSubmit

2개의 함수를 인자로 갖는다.

onValid 함수는 form이 유효할 때 실행되고, inInvalid 함수는 form이 유효하지 않을 때 실행된다.

<form onSubmit={handleSubmit(onValid,onInvalid)}>
  <input type="text" {...register("firstName")} />
  <input type="text" {...register("lastName")} />
  <input type="email" {...register("email")} />
  <input type="submit" />
</form>

 

 

reset / resetField

rest the entire form state, fields reference, subscriptions or an individual field state

resetField("firstName")
resetField("firstName", { keepError: true })}
resetField("firstName", { keepDirty: true })}
resetField("firstName", { defaultValue: "New" })}

 

mode

validation이 발생하는 시점을 설정할 수 있다. 

const { register } = useForm<LoginForm>({ mode: "onSubmit" });
  • onSubmit
  • onChange
  • onBlur: input에서 벗어나 다른 곳을 클릭했을 때
  • onTouched: 가장 처음 onBlur가 발생했을 때 검사하고, 이후에는 매번 ref 값이 바뀔 때마다
  • all

 

setError

the function allows you to manually set one or more errors

export default function ReactHookForm(){
    const { setError, formState: { errors }} = useForm<FormInputs>();

    useEffect(() => {
        setError("username", {
          type: "manual",
          message: "Dont Forget Your Username Should Be Cool!",
        })
    }, [setError]);

    return(
        <>
            <form onSubmit={handleSubmit(onSubmit)}>
              <input {...register("username")} />
              {errors.username && <p>{errors.username.message}</p>}

              <input type="submit" />
            </form>

            // 여러 에러 설정 
            <button
              type="button"
              onClick={() => {
                const inputs = [
                  {
                    type: "manual",
                    name: "username",
                    message: "Double Check This",
                  },
                  {
                    type: "manual",
                    name: "firstName",
                    message: "Triple Check This",
                  },
                ]

                inputs.forEach(({ name, type, message }) => {
                  setError(name, { type, message })
                })
              }}
            >
            Trigger Name Errors
            </button>
        </>
    )
}

 

trigger

validation을 trigger한다. 

<button type="button" onClick={()=>{trigger("lastName")}}> 
  Trigger
</button>

 

setValue

input의 register값을 특정 value로 set 해줄 때 사용한다. 

 

 

기존 프로젝트에 useForm을 적용하다가 한참을 헤맨 부분이다. 

'동' select 태그의 key를 districtValue로 설정하여, 다른 구를 선택했을 때 '동' select 태그가 리렌더링 되도록 했다.

이때 '동' select UI에 '동 선택'이 아니라 빈값이 나와서 로그를 찍어보니 'dong' value가 이전 '구'에 해당하는 '동'으로 되어서였다.

setValue를 통해 'dong'값을 기본값인 ''으로 설정해서 해결할 수 있었다. 

 

 

참고

https://react-hook-form.com/docs/useform