Skip to content

자주 묻는 질문

자주 묻는 질문

React Hook Form 의 퍼포먼스

이 커스텀 훅을 만들 때 가장 먼저 고려된 목표 중 하나가 퍼포먼스입니다. React Hook Form 은 비제어 컴포넌트를 활용하고 있기 때문에 ref 에서 register 함수가 실행됩니다. 이러한 접근 방식은 사용자가 타이핑하거나 값을 변경할 때 리랜더링이 일어나는 양을 줄여줄 것입니다. 제어 컴포넌트가 아니기 때문에 페이지에 컴포넌트가 마운트되는 속도도 훨씬 더 빠릅니다. 마운트되는 속도에 대해 여러분이 참고하실 수 있도록 간단한 속도 비교 테스트를 이 저장소에 올려 두었습니다.


접근성 있는 입력 에러와 메세지를 어떻게 만드나요?

React Hook Form 은 비제어 컴포넌트를 기반으로 하고 있으므로, 접근성이 높은 커스텀 폼을 쉽게 만들 수 있습니다.

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

export default function App() {
  const { register, handleSubmit, formState: { errors } } = useForm();
  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label htmlFor="firstName">First name</label>
      <input
        id="firstName"
        aria-invalid={errors.firstName ? "true" : "false"}
        {...register('firstName', { required: true })}
      />
      {errors.firstName && (
        <span role="alert">
          This field is required
        </span>
      )}

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

클래스 컴포넌트와 사용할 수 있나요?

아니오. 사용할 수 없습니다만, 클래스 컴포넌트를 감싸는 래퍼를 만들 수는 있습니다.

여러분은 클래스 컴포넌트 안에서 훅을 사용할 수 없습니다. 하지만 확실히 클래스 컴포넌트와 훅을 사용하는 함수 컴포넌트를 같은 트리 안에서 섞어 사용할 수 있습니다. 컴포넌트가 클래스인지, 훅을 사용하는 함수 컴포넌트인지는 개별 컴포넌트의 세부 구현에 불과합니다. 긴 안목으로 보아 우리는 사람들이 훅을 우선적으로 고려하여 리액트 컴포넌트를 작성하길 기대합니다.

폼을 어떻게 리셋하나요?

폼을 초기화하는데 두 가지 방법이 있습니다.

  • HTMLFormElement.reset()

    이 메서드는 폼의 리셋 버튼을 누르는 것과 똑같이 동작하지만, 오로지 input/select/checkbox 값들만 초기화합니다.

  • React Hook Form API: reset()

    React Hook Form 의 reset 메서드는 모든 필드 값을 리셋하며, 또한 폼 안의 모든 errors 를 초기화합니다.


어떻게 폼의 기본값을 설정하나요?

React Hook Form 은 비제어 컴포넌트를 활용합니다. 비제어 컴포넌트를 사용하면 defaultValuedefaultChecked 값을 개별 필드에 넣어 기본값을 설정할 수 있습니다. 하지만 훅에서 기본적으로 손쉽게 모든 인풋의 기본값을 설정할 수 있는 방법을 제공합니다. 아래의 예제를 보세요.

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

export default function App() {
  const { register, handleSubmit } = useForm({
    defaultValues: {
      firstName: "bill",
      lastName: "luo",
      email: "bluebill1049@hotmail.com"
    }
  });
  const onSubmit = data => console.log(data);

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

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

ref 를 공유할 수 있나요?

React Hook Form 은 입력 값을 모으기 위해 ref 를 필요로 합니다. 하지만 ref 를 다른 목적으로 (예: 해당 뷰로 스크롤하기) 활용하고 싶을 수도 있습니다. 아래의 예제로 그 방법을 확인해보세요.

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

export default function App() {
  const { register, handleSubmit } = useForm();
  const firstNameRef = useRef(null);
  const onSubmit = data => console.log(data);
  const { ref, ...rest } = register('firstName');

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...rest} name="firstName" ref={(e) => {
        ref(e)
        firstNameRef.current = e // you can still assign to ref
      }} />

      <button>Submit</button>
    </form>
  );
}import React, { useRef } from "react";
import { useForm } from "react-hook-form";

type Inputs = {
  firstName: string,
  lastName: string,
};

export default function App() {
  const { register, handleSubmit } = useForm<Inputs>();
  const firstNameRef = useRef<HTMLInputElement | null>(null);
  const onSubmit = data => console.log(data);
  const { ref, ...rest } = register('firstName');

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...rest} name="firstName" ref={(e) => {
        ref(e)
        firstNameRef.current = e // you can still assign to ref
      }} />

      <button>Submit</button>
    </form>
  );
}

만약에 ref 에 접근할 수 없다면 어떻게 하죠?

사실 ref 없이 register 를 할 수 있습니다. 수동으로 setValue, setError 그리고 trigger 를 사용하면 됩니다.

참고: ref 가 등록되지 않았기 때문에, React Hook Form 은 인풋에 이벤트 리스너를 등록할 수 없을 겁니다. 따라서 인풋 값과 에러를 수동으로 업데이트 해 주어야 합니다.

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

export default function App() {
  const { register, handleSubmit, setValue, setError } = useForm();
  const onSubmit = data => console.log(data);

  useEffect(() => {
    register("firstName", { required: true });
    register("lastName");
  }, []);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        name="firstName"
        onChange={e => setValue("firstName", e.target.value)}
      />
      <input
        name="lastName"
        onChange={e => {
          const value = e.target.value;
          if (value === "test") {
            setError("lastName", "notMatch")
          } else {
            setValue("lastName", e.target.value)
          }
        }}
      />
      <button>Submit</button>
    </form>
  );
}

브라우저 호환성은 어떤가요?

React Hook Form 은 모든 메이저 브라우저를 지원합니다.

오래된 IE11 를 지원하려면, react-hook-form IE11 버전을 불러와서 사용할 수 있습니다.

import { useForm } from 'react-hook-form/dist/index.ie11'; // V6
import { useForm } from 'react-hook-form/dist/react-hook-form.ie11'; // V5'
// Resolvers
import { yupResolver } from '@hookform/resolvers/dist/ie11/yup';

왜 첫 번째 키 입력이 동작하지 않을까요?

value 대신에 defaultValue 를 사용하고 있는지 다시 확인해주세요.

React Hook Form 은 비제어 컴포넌트를 활용하기 때문에 onChange 를 사용하여 state 를 바꾸고, 그 값을 인풋의 value 에 반영해줄 필요가 없습니다. 따라서 value 자체가 필요 없습니다. 사실 초기 값을 지정하고자 할 때 defaultValue 만 넣어주면 됩니다.


MutationObserver 때문에 테스트가 실패하는데요?

만약에 테스트하는데 어려움을 겪고 계시다면 MutationObserver 때문입니다. mutationobserver-shim 패키지를 설치하고 setup.js 파일에서 불러오세요.


React Hook Form 을 Formik, Redux Form 과 비교한다면?

먼저, 모든 라이브러리들은 폼을 만드는 과정을 쉽고 좋게 만들겠다는 공통의 문제를 해결하려고 합니다. 하지만 세 라이브러리는 약간 근본적인 차이를 가지고 있고, react-hook-form 는 기본적으로 비제어 컴포넌트를 활용하고 있습니다. 그리하여 여러분의 폼이 최대한의 퍼포먼스를 내고 최소한의 리랜더링만 발생하도록 합니다. 그 위에 react-hook-form 은 리액트 훅을 기반으로 만들어졌으며 훅으로 사용됩니다. 따라서 별도의 컴포넌트를 불러올 필요가 없습니다. 아래에 더 자세한 차이점을 표기했습니다.

React Hook Form

Formik

Redux Form

컴포넌트비제어 & 제어제어제어
랜더링최소한의 리랜더링인풋에 타이핑하면서 지역 상태가 변할떄마다 리랜더링인풋에 타이핑하면서 상태 관리 라이브러리(Redux)의 상태가 바뀔 때마다 리랜더링
API컴포넌트 (RenderProps, Form, Field) + 훅컴포넌트 (RenderProps, Form, Field)
패키지 크기작음
react-hook-form@6.0.0
8.9KB
중간
formik@2.1.4
15KB

redux-form@8.3.6
26.4KB
유효성 검사내장됨, Yup, Joi, Superstruct 혹은 직접 제작 가능직접 만들어야 하거나 Yup직접 만들어야 하거나 플러그인
러닝 커브낮음중간중간
현황중간 수준의 커뮤니티이며 성장 중큰 커뮤니티: 커뮤니티 안에서 잘 정립된 폼 라이브러리큰 커뮤니티: 커뮤니티 안에서 잘 정립된 폼 라이브러리

제어 컴포넌트와 조합할 수 있나요?

짧게 답변드리면, 할 수 있습니다.

React-hook-form 은 사용자가 제어되는 폼을 만들도록 권장하진 않지만 제어 컴포넌트와 쉽게 조합할 수 있습니다.

그 방법은 watch API 를 사용하여 각 인풋 값의 변화를 관찰하고 value prop 에 할당하는 것입니다.

또는 감싸는 컴포넌트인 Controller 을 사용하여 커스텀 레지스터를 관리 할 수 ​​있습니다.

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

function App() {
  const { control } = useForm();
  
  return (
    <Controller
      render={({ field }) => <input {...field} />}
      name="firstName"
      control={control}
      defaultValue=""
    />
  );
}
import React, { useEffect } from "react";
import { useForm } from "react-hook-form";

function App() {
  const { register, watch, setValue, handleSubmit } = useForm({
    defaultValues: {
      firstName: "",
      lastName: ""
    }
  });
  const { firstName, lastName } = watch();

  useEffect(() => {
    register("firstName");
    register("lastName");
  }, [register]);

  const handleChange = (e, name) => {
    setValue(name, e.target.value);
  };

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        onChange={(e) => handleChange(e, "firstName")}
        value={firstName}
      />

      <input onChange={(e) => handleChange(e, "lastName")} value={lastName} />
      <input type="submit" />
    </form>
  );
}

React Hook Form 테스트하기

  • 왜 리액트 네이티브 환경(react-native-testing-library)에서 테스트가 되지 않나요?

    React Hook Form 은 서버 사이드 랜더링 중에는 인풋을 등록할 수 없습니다. 따라서 리액트 네이티브 환경에서 테스트 할 때 window 객체가 undefined 가 됩니다. 간단히 고치는 방법으로 window 객체를 스텁(stub)으로 만들면 등록이 가능합니다.

  • act 경고가 뜨나요?

    React Hook Form 의 모든 유효성 검사 메서드는 async 함수로 처리됩니다. 따라서 act 함수 실행 시 async 를 감싸주는 것이 중요합니다.

  • 왜 입력값을 바꿀 때 이벤트가 발생하지 않나요?

    React Hook Form 은 입력값이 바뀔 때 input 이벤트를 사용합니다. 만약 react-testing-library 를 사용하고 계시다면, 손쉽게fireEvent.input 로 바꾸면 됩니다. 여기 예제 링크가 있습니다.

    만약 enzyme 를 사용하고 계시다면, 인풋의 DOM 노드에 수동으로 value 를 설정해주시고, input 이벤트를 실행하셔야 합니다.

    const element = wrapper.find("select[data-testid='a']");
    element.getDOMNode().value = "foo";
    element.getDOMNode().dispatchEvent(new Event("input"));

여러분의 도움이 필요합니다

React Hook Form 이 유용한 프로젝트라고 생각하신다면, Star 를 눌러서 저장소와 기여하는 분들을 응원해주세요 ❤

Edit