Skip to content

FAQs

자주 묻는 질문

React Hook Form의 성능

성능은 이 라이브러리가 만들어지게 된 주요 이유 중 하나입니다. React Hook Form은 비제어 폼(uncontrolled form)에 의존하며, 이 때문에 register 함수가 ref를 캡처하고, 제어 컴포넌트는 Controller 또는 useController를 통해 리렌더링 범위를 갖습니다. 이 접근 방식은 사용자가 입력 필드에 타이핑하거나 폼의 루트에서 다른 폼 값이 변경될 때 발생하는 리렌더링 횟수를 줄입니다. 컴포넌트는 제어 컴포넌트보다 오버헤드가 적기 때문에 페이지에 더 빠르게 마운트됩니다. 참고로, 이 레포 링크에서 간단한 비교 테스트를 확인할 수 있습니다.


접근 가능한 입력 오류와 메시지를 만드는 방법

React Hook Form은 비제어 컴포넌트(Uncontrolled Components)를 기반으로 하여, 접근 가능한 커스텀 폼을 쉽게 구축할 수 있게 해줍니다.
(비제어 컴포넌트에 대한 더 자세한 정보는 컴포넌트 간 상태 공유를 참고하세요.)

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">이름</label>
<input
id="firstName"
aria-invalid={errors.firstName ? "true" : "false"}
{...register("firstName", { required: true })}
/>
{errors.firstName && <span role="alert">이 필드는 필수입니다</span>}
<input type="submit" />
</form>
)
}

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

기본적으로는 지원하지 않습니다. 클래스 컴포넌트에서 사용하려면 래퍼를 만들어서 사용해야 합니다.

클래스 컴포넌트 내부에서는 훅을 사용할 수 없지만, 클래스 컴포넌트와 훅을 사용하는 함수 컴포넌트를 하나의 트리에서 혼합하여 사용할 수 있습니다. 컴포넌트가 클래스인지 훅을 사용하는 함수인지는 단순히 그 컴포넌트의 구현 방식일 뿐입니다. 장기적으로는 훅이 React 컴포넌트를 작성하는 주요 방식이 될 것으로 예상됩니다.


폼을 초기화하는 방법

폼을 초기화하는 방법은 두 가지가 있습니다:

  • HTMLFormElement.reset()

    이 메서드는 폼의 리셋 버튼을 클릭하는 것과 동일한 동작을 수행합니다. input, select, checkbox 값만 초기화합니다.

  • React Hook Form API: reset()

    React Hook Form의 reset 메서드는 모든 필드 값을 초기화하고, 폼 내의 모든 errors도 함께 지웁니다.


폼 값 초기화 방법

React Hook Form은 비제어 폼을 기반으로 동작하기 때문에, 각 필드에 defaultValuedefaultChecked를 지정할 수 있습니다. 하지만 일반적으로 useFormdefaultValues를 전달하여 폼을 초기화하는 방법을 더 많이 사용하고 권장합니다.

function App() {
const { register, handleSubmit } = useForm({
defaultValues: {
firstName: "bill",
lastName: "luo",
},
})
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<input {...register("firstName")} />
<input {...register("lastName")} />
<button type="submit">Submit</button>
</form>
)
}

비동기 기본값을 사용하려면 다음 방법을 활용할 수 있습니다:

  • 비동기 defaultValues

    function App() {
    const { register, handleSubmit } = useForm({
    defaultValues: async () => {
    const response = await fetch("/api")
    return await response.json() // return { firstName: '', lastName: '' }
    },
    })
    }
  • 반응형 values

    function App() {
    const { data } = useQuery() // data returns { firstName: '', lastName: '' }
    const { register, handleSubmit } = useForm({
    values: data,
    resetOptions: {
    keepDirtyValues: true, // 변경된 필드는 유지하고, defaultValues만 업데이트
    },
    })
    }

ref 사용법 공유하기

React Hook Form은 입력 값을 수집하기 위해 ref가 필요합니다. 하지만 ref를 다른 목적으로 사용하고 싶을 수도 있습니다. 예를 들어, 화면으로 스크롤하거나 포커스를 맞추는 경우가 있습니다.

import { useRef, useImperativeHandle } 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)
const onSubmit = (data: Inputs) => console.log(data)
const { ref, ...rest } = register("firstName")
const onClick = () => {
firstNameRef.current!.value = ""
}
useImperativeHandle(ref, () => firstNameRef.current)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...rest} ref={firstNameRef} />
<button type="button" onClick={onClick}>
clear
</button>
<button>Submit</button>
</form>
)
}
import { useRef, useImperativeHandle } 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")
const onClick = () => {
firstNameRef.current!.value = ""
}
useImperativeHandle(ref, () => firstNameRef.current)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...rest} ref={firstNameRef} />
<button type="button" onClick={onClick}>
clear
</button>
<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>
)
}

첫 번째 키 입력이 동작하지 않는 이유는 무엇인가요?

value를 사용하지 않았는지 확인하세요. 올바른 속성은 defaultValue입니다.

React Hook Form은 비제어 입력(uncontrolled inputs)에 초점을 맞추고 있습니다. 이는 onChange를 통해 state로 입력 value를 변경할 필요가 없다는 의미입니다. 사실, value는 전혀 필요하지 않습니다. 초기 입력 값에는 defaultValue만 설정하면 됩니다.

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"

React Hook Form, Formik, Redux Form 비교

먼저, 이 세 라이브러리는 모두 동일한 문제를 해결하려고 합니다: 폼 구축 경험을 가능한 한 쉽게 만드는 것입니다. 하지만 이 세 가지 라이브러리 사이에는 근본적인 차이점이 있습니다. react-hook-form은 비제어 입력(uncontrolled inputs)을 염두에 두고 설계되었으며, 최고의 성능과 최소한의 리렌더링을 제공하려고 합니다. 또한, react-hook-form은 React Hooks를 기반으로 만들어졌으며, 훅으로 사용되기 때문에 컴포넌트를 임포트할 필요가 없습니다. 아래는 세 라이브러리의 주요 차이점을 정리한 표입니다:

React Hook FormFormikRedux Form
컴포넌트비제어(uncontrolled) & 제어(controlled)제어(controlled)제어(controlled)
렌더링최소한의 리렌더링과 계산 최적화로컬 상태 변경에 따른 리렌더링 (입력 시마다 리렌더링)상태 관리 라이브러리(Redux) 변경에 따른 리렌더링 (입력 시마다 리렌더링)
API훅(Hooks)컴포넌트 (RenderProps, Form, Field) + 훅컴포넌트 (RenderProps, Form, Field)
패키지 크기작음
react-hook-form@7.27.0
8.5KB
중간
formik@2.1.4
15KB

redux-form@8.3.6
26.4KB
유효성 검사내장, Yup, Zod, Joi, Superstruct 및 커스텀 유효성 검사 지원직접 구현 또는 Yup직접 구현 또는 플러그인
학습 곡선낮음에서 중간중간중간

watch vs getValues vs state

  • watch: 모든 입력 또는 특정 입력의 변경 사항을 이벤트 리스너를 통해 구독하고, 구독된 필드에 따라 리렌더링을 수행합니다. 실제 동작을 확인하려면 이 codesandbox를 참고하세요.
  • getValues: 커스텀 훅 내부에 저장된 값을 참조로 가져옵니다. 빠르고 가볍게 동작하며, 이 메서드는 리렌더링을 트리거하지 않습니다.
  • local state: React의 로컬 상태는 단순히 입력 상태뿐만 아니라 무엇을 렌더링할지도 결정합니다. 이는 각 입력 변경 시마다 트리거됩니다.

삼항 연산자로 기본값이 제대로 변경되지 않는 이유는 무엇인가요?

React Hook Form은 여러분의 전체 폼과 입력 필드를 제어하지 않습니다. 그렇기 때문에 React는 실제 입력 필드가 교체되었는지 인식하지 못합니다. 이 문제를 해결하려면 입력 필드에 고유한 key prop을 제공하면 됩니다. Kent C. Dodds가 작성한 이 글에서 key prop에 대해 더 자세히 알아볼 수 있습니다.

import React from "react"
import { useForm } from "react-hook-form"
export default function App() {
const { register } = useForm()
return (
<div>
{watchChecked ? (
<input {...register("input3")} key="key1" defaultValue="1" />
) : (
<input {...register("input4")} key="key2" defaultValue="2" />
)}
</div>
)
}

모달 또는 탭 폼 작업 방법

React Hook Form은 각 입력 내부에 입력 상태를 저장함으로써 네이티브 폼 동작을 따릅니다(단, useEffect에서 커스텀 register를 사용하는 경우는 제외). 일반적인 오해 중 하나는 입력 상태가 마운트되거나 언마운트된 입력과 함께 유지된다는 것입니다. 예를 들어 모달이나 탭 폼을 작업할 때 이런 오해가 발생할 수 있습니다. 대신, 올바른 해결책은 각 모달이나 탭 내부에 새로운 폼을 구축하고, 제출 데이터를 로컬 또는 전역 상태로 캡처한 후 결합된 데이터로 작업을 수행하는 것입니다.

또는 useForm을 호출할 때 더 이상 사용되지 않는 옵션인 shouldUnregister: false를 사용할 수도 있습니다.

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이 프로젝트에서 유용하다면, GitHub에서 스타를 눌러 지원해 주세요.