data life

[React] React-Hook-Form에 대해 알아보기 본문

Front-end/React

[React] React-Hook-Form에 대해 알아보기

주술회전목마 2023. 6. 21. 03:23


이번 프로젝트 로그인/회원가입 구현을 맡으면서 사용한 react-hook-form에 대해 정리해보려고 합니다.

react-hook-form은 form의 validation을 도와주는 라이브러리로 성능적으로도 더 뛰어나다고 합니다만 자세한 건 공식문서를 참고해보시는 걸로!!

 

📌 참고로

- 쓰면서 굉장히 유용했고 자주 이용했던 부분 위주로 설명할 예정입니다.
- 공식문서는 다크모드로 보시길 추천드립니다.

 

 

 

1. useForm

form을 만들기 위해서는 useForm을 이용해야합니다.

const { register } = useForm({
  mode: 'onSubmit',
  reValidateMode: 'onChange',
  defaultValues: {},
  resolver: undefined,
  context: undefined,
  criteriaMode: "firstError",
  shouldFocusError: true,
  shouldUnregister: false,
  shouldUseNativeValidation: false,
  delayError: undefined
})

- mode : 동작을 제출하기 전에 검증하는 방법 ⭐️

- reValidateMode : 동작을 제출한 후 검증하는 방법

- defaultValues : 초기값 설정 ⭐️

- resolver : 커스텀한 유효성 검사 로직 사용 가능

- context : 유효성 검사기(resolver)에 전달할 컨텍스트를 설정합니다. 일반적으로 유효성 검사기가 다른 데이터나 함수에 접근해야 할 때 사용됩니다.

- criteriaMode : 여러 개의 에러가 발생했을 때 어떤 에러를 우선하여 보여줄지를 설정

- shouldFocusError : 유효성 검사에 실패한 필드에 자동으로 포커스를 맞출지 여부를 설정합니다. true로 설정하면 실패한 필드에 포커스가 이동됩니다.

- shouldUnregister : 등록된 폼 필드를 언제 해제할지를 설정합니다. false로 설정하면 폼이 리셋되어도 등록된 필드는 유지됩니다.

- shouldUseNativeValidation : 브라우저 기본 유효성 검사를 사용할지 여부를 설정합니다. false로 설정하면 React Hook Form의 내장 유효성 검사를 사용합니다.

- delayError : 에러 메시지를 보여주는 딜레이를 설정합니다. 일정 시간이 지난 후에 에러 메시지를 보여주고자 할 때 사용됩니다.

 

mode / reValidateMode 옵션들

  1. onBlur 모드: 입력 필드가 포커스를 잃을 때 유효성 검사가 수행됩니다. 이는 유저에게 폼을 작성하는 도중에 계속적으로 오류 메시지가 나타나는 것을 방지하여 좋은 사용자 경험을 제공합니다. 특히 복잡하거나 긴 폼에서 유용
  2. onChange 모드: 사용자가 입력을 하는 즉시 유효성 검사가 수행됩니다. 이는 즉각적인 피드백을 제공하여, 간단한 폼에 유용할 수 있습니다.
  3. onSubmit 모드 : 사용자가 폼을 제출할 때만 유효성 검사가 수행됩니다. 사용자가 제출 버튼을 클릭하거나 폼을 제출할 때ㄲ까지 어떠한 간섭을 하지 않습니다.

다음은 onBlur모드를 이용한 로그인 화면입니다.



 

 

useForm을 도와주는 함수에 대해 알아보도록 합시다.

const {
	register
	unregister
	formState
	watch
	handleSubmit
	reset
	resetField
	setError
	clearErrors
	setValue
	setFocus
	getValues
	getFieldState
	trigger
	control
  } = useForm();

 

1. register

각각의 폼 입력 필드와 연결되어, 그 필드의 값들을 추적하고 관리하는데 사용됩니다.

const { register } = useForm();

<input {...register("firstName")} />

register 함수에 전달하는 문자열 인자는 해당 입력 필드의 이름입니다. 이름을 통해 폼의 다른 부분에서 해당 입력 필드의 값을 참조하고 변경할 수 있습니다.

register 함수를 통해 입력 필드에 더 많은 설정을 추가할 수 있습니다. 예를 들어, 필수 입력 필드를 설정하거나 유효성 검사 규칙을 추가할 수 있습니다. 만약 필드 값이 이러한 규칙을 위반하면 react-hook-form은 해당 필드의 에러 상태를 관리하고, 이를 사용하여 에러 메시지를 표시할 수 있습니다.

<input {...register("firstName", { required: true })} />
<input {...register("age", { min: 18, max: 99 })} />
<input
      {...register('password', {
        required: '비밀번호는 필수 입력입니다.',
        pattern: {
          value: /^(?=.*?[A-Za-z])(?=.*?[0-9]).{6,}$/,
          message:
            '영문, 숫자를 포함한 6자 이상의 비밀번호를 입력해주세요.',
        },
      })}
      errors={errors}
/>

- required 

- min/maxLength

- min/max

- pattern

- validate

등등..

이외는 공식문서 참조

 

2. unregister

 

3. formState

현재 폼 상태에 대한 여러 정보를 포함하고 있습니다.

formState 객체에는 여러 속성이 있습니다

  1. isDirty: 사용자가 어느 한 입력 필드를 수정했는지 여부를 나타냅니다.
  2. isValid: 현재 폼 값이 모든 유효성 검사 규칙을 통과했는지 여부를 나타냅니다.
  3. errors: 현재 폼의 각 필드에서 반환된 유효성 검사 오류입니다.
  4. isSubmitted: 폼이 이미 제출되었는지 여부를 나타냅니다.
  5. isSubmitting: 폼이 현재 제출 중인지 여부를 나타냅니다.
  6. isTouched: 사용자가 어느 한 입력 필드를 터치했는지 여부를 나타냅니다.

이러한 속성들은 폼의 현재 상태에 따라 UI를 업데이트하거나 특정 동작을 실행하는 데 유용합니다. 예를 들어, isValid 속성은 폼이 유효한 경우에만 제출 버튼을 활성화하는 데 사용할 수 있습니다.

const { formState: { isValid } } = useForm();

<button disabled={!isValid}>Submit</button>

또한 errors 속성을 사용하여 각 필드에 대한 오류 메시지를 표시할 수 있습니다.

const { register, formState: { errors } } = useForm();

<input {...register("firstName", { required: "필수 입력 값입니다." })} />
{errors.firstName && <p>{errors.firstName.message}</p>}

 

4. watch

입력 필드의 상태를 실시간으로 확인하는데 사용됩니다. watch는 모든 입력의 상태를 관찰하며, 필드의 변경 사항이 있을 때마다 새로운 값을 반환합니다.

❗️ watch는 입력 필드의 상태를 실시간으로 감시하므로, 복잡한 폼에서는 성능에 영향을 줄 수 있으니 주의해서 사용해야 합니다.

 

특정 필드 감시: watch 함수에 필드 이름을 인수로 전달하여 특정 필드를 감시할 수 있습니다.

const { register, watch } = useForm();
const firstName = watch("firstName");

모든 필드 감시: watch 함수에 인수를 전달하지 않으면 모든 필드를 감시합니다. 이 경우, watch는 입력 필드의 전체 상태 객체를 반환합니다.

const { register, watch } = useForm();
const allFields = watch();

여러 필드 감시: watch 함수에 필드 이름의 배열을 전달하여 여러 필드를 동시에 감시할 수 있습니다.

const { register, watch } = useForm();
const [firstName, lastName] = watch(["firstName", "lastName"]);

 

5. handleSubmit

폼의 제출 이벤트를 처리하는데 사용됩니다. 이 함수는 콜백 함수를 인수로 받으며, 폼이 유효한 경우에만 이 콜백 함수를 실행합니다.

콜백 함수는 폼 데이터를 인수로 받습니다. 폼 데이터는 입력 필드의 이름을 키로 가지고 있는 객체입니다. 

handleSubmit 함수는 자동으로 event.preventDefault()를 호출하여 페이지의 자동 리프레시를 막고, 입력된 데이터를 수집한 후 유효성을 검사합니다. 유효성 검사를 통과하면, handleSubmit는 콜백 함수를 실행합니다.

export default function App() {
  const { register, handleSubmit } = useForm();

  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstName" ref={register} />
      <input name="lastName" ref={register} />

      <button type="submit">Submit</button>
    </form>
  );
}

 

또한, handleSubmit는 선택적으로 두 개의 콜백 함수를 더 받을 수 있습니다. 두 번째 콜백 함수는 유효성 검사에서 오류가 발생했을 때 실행되고, 세 번째 콜백 함수는 폼이 제출될 때 항상 실행됩니다.

handleSubmit(onSubmit, onError, onFinally);

- onSubmit : 유효성 검사를 통과했을 때 실행되는 콜백

- onError : 유효성 검사에서 오류가 발생했을 때 실행되는 콜백

- onFinally : 폼 제출 후 항상 실행되는 콜백

 

6. reset

폼의 입력 필드를 초기 상태로 되돌리는데 사용됩니다. 즉, 입력 값, 오류, 더티 필드, 터치된 필드 등의 상태가 모두 초기화 됩니다.  

❗️ 그러나, useFormdefaultValues를 변경하지 않습니다.

function MyForm() {
  const { register, handleSubmit, reset } = useForm();

  const onSubmit = data => {
    console.log(data);

    // submit 후 form reset
    reset();
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("firstName")} defaultValue="test" />
      <input {...register("lastName")} />
      <button type="submit">Submit</button>
    </form>
  );
}

 

7. setError

특정 입력 필드에 프로그래밍 방식으로 오류를 설정할 수 있습니다. 이는 서버에서 오류 메시지를 받아 폼에 적용하거나, 사용자 정의 유효성 검사기를 사용하여 오류를 설정할 때 유용합니다.

function MyForm() {
  const { register, handleSubmit, setError } = useForm();

  const onSubmit = data => {
    if (/* some condition */) {
      setError("firstName", {
        type: "manual",
        message: "First name is required"
      });
    }
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("firstName")} />
      <button type="submit">Submit</button>
    </form>
  );
}

첫 번째 인자는 오류를 설정할 필드의 이름입니다.

두 번째 인자는 오류 객체로, 이는 type과 message 속성을 포함할 수 있습니다.

- type : 오류의 유형을 나타내는 문자열

- message : 사용자에게 보여줄 오류 메시지

 

8. clearError

setError를 통해 설정된 특정 입력 필드의 오류 상태를 지울 수 있습니다.

 

9. setValue

폼의 특정 필드에 값을 설정하는데 사용됩니다. 이는 상태 관리 없이 직접적으로 입력 필드에 값을 설정할 수 있게 해줍니다.

 

  1. name: 값을 설정하려는 필드의 이름입니다.
  2. value: 설정하려는 값입니다.
  3. options: 추가적인 옵션 객체로 shouldValidate와 shouldDirty라는 선택적 프로퍼티를 포함하고 있습니다.
    - shouldValidate는 설정 후 유효성 검사를 수행할지 여부를 설정하며,
    - shouldDirty는 필드를 'dirty' 상태로 설정할지 여부를 결정합니다.

❓dirty 란?

주로 사용자가 어떤 형태로든 입력 필드에 대한 변경을 수행했을 때 사용되는 용어로, 이는 사용자의 상호작용이 발생했음을 나타내는 방법 중 하나입니다. 따라서, 폼의 특정 필드가 "dirty"라는 것은 해당 필드에 대한 사용자의 입력이 한 번 이상 발생했음을 의미합니다.

function MyForm() {
  const { register, handleSubmit, setValue } = useForm();

  const onSubmit = data => {
    // 제출 로직
  };

  const changeValue = () => {
    setValue("firstName", "new value", { shouldValidate: true, shouldDirty: true });
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("firstName")} />
      <button type="button" onClick={changeValue}>Change Value</button>
      <button type="submit">Submit</button>
    </form>
  );
}

"firstName" 필드의 값을 "new value"로 설정하며, 이때 해당 필드는 유효성 검사를 수행하고 'dirty' 상태로 표시됩니다.

 

10. setFocus

특정 폼 필드에 포커스를 설정할 수 있습니다.

setFocus('fieldName');

지정된 필드 'fieldName' 에 포커스가 설정됩니다. 이것은 주로 사용자의 주의를 특정 필드로 유도하거나, 폼의 유효성 검사에서 오류가 발생했을 때 해당 필드로 사용자를 안내하는 데 유용합니다.

 

11. getValues

현재 폼 필드의 값들을 얻을 수 있습니다.

폼의 상태를 검사하거나 특정 작업을 수행하기 전에 현재의 폼 값들을 확인하는 데 getValues 함수가 유용하게 사용됩니다.

const [username, email] = getValues(['username', 'email']);
console.log(username, email);

 

12. getFieldState (v7 이상)

해당 필드의 상태 정보를 반환합니다. 폼의 특정 필드에 대한 상세한 상태 정보를 얻고 싶을 때 유용하게 사용됩니다. 특히 유효성 검사와 관련된 로직을 처리할 때 이 함수를 활용할 수 있습니다.

  • isDirty: 사용자가 필드를 수정한 후라면 true, 그렇지 않다면 false.
  • isTouched: 사용자가 필드를 클릭한 적이 있으면 true, 그렇지 않다면 false.
  • isValid: 해당 필드가 유효성 검사를 통과하면 true, 그렇지 않다면 false.
  • error: 해당 필드에 대한 에러 메시지가 있다면 그 메시지를, 그렇지 않다면 undefined.

 

13. trigger

주어진 필드나 필드들의 유효성을 검사합니다. 이 메서드는 useForm hook에서 반환되며, 이를 사용해 특정 필드 또는 전체 필드의 유효성 검사를 수동으로 실행할 수 있습니다.

function Form() {
  const { register, trigger } = useForm();

  return (
    <form>
      <input {...register("username", { required: true })} />

      <button onClick={() => trigger("username")}>검사</button>
    </form>
  );
}

export default Form;

trigger 메서드를 사용해 유효성 검사를 실행하고, 그 결과에 따라 추가적인 로직을 실행할 수 있습니다.

 

14. control

Controller 컴포넌트에서 사용되며, 비제어 컴포넌트를 제어 컴포넌트처럼 동작하게 할 수 있도록 합니다.

이는 리액트의 기본적인 Form 동작에서 벗어나는 특수한 경우나, 외부 UI 라이브러리와 함께 사용할 때 유용합니다.

 

Controller 컴포넌트의 여러 props

  • name: 컨트롤하는 필드의 이름(필수)
  • control: useForm으로부터 반환된 control 객체(필수)
  • rules: 유효성 검사 규칙
  • defaultValue: 필드의 기본 값
  • render: 필드를 렌더링하는 함수

Controller의 render prop은 필드를 렌더링하는 함수를 받습니다.

이 함수는 { field, fieldState, formState } 객체를 인자로 받습니다.

  • field는 { onChange, onBlur, name, ref, value }와 같은 필드와 관련된 속성을 가지고 있습니다. 이는 <input />과 같은 입력 요소에 바인딩해 사용합니다.
  • fieldState는 해당 필드의 상태 (error, isTouched 등)를 가지고 있습니다.
  • formState는 전체 폼의 상태 (isDirty, isValid 등)를 가지고 있습니다.

이러한 속성들을 이용해 우리는 필드를 커스텀하게 렌더링할 수 있습니다.

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

function Form() {
  const { handleSubmit, control } = useForm();

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <Controller 
        name="firstName"
        control={control}
        defaultValue=""
        render={({ field }) => <input {...field} />}
      />
      <Controller 
        name="lastName"
        control={control}
        defaultValue=""
        render={({ field }) => <input {...field} />}
      />
      <input type="submit" />
    </form>
  );
}

export default Form;