설치
React Hook Form을 설치하는 것은 단 한 번의 명령어로 가능합니다.
npm install react-hook-form
예제
다음 코드 예제는 기본 사용법을 보여줍니다:
import { useForm, SubmitHandler } from "react-hook-form"type Inputs = {example: stringexampleRequired: string}export default function App() {const {register,handleSubmit,watch,formState: { errors },} = useForm<Inputs>()const onSubmit: SubmitHandler<Inputs> = (data) => console.log(data)console.log(watch("example")) // 입력 값 관찰return (/* "handleSubmit"은 "onSubmit"을 호출하기 전에 입력 값을 validation합니다. */<form onSubmit={handleSubmit(onSubmit)}>{/* "register" 함수를 호출하여 입력을 훅에 register합니다. */}<input defaultValue="test" {...register("example")} />{/* HTML 표준 validation 규칙을 사용하여 입력 validation을 포함합니다. */}<input {...register("exampleRequired", { required: true })} />{/* field validation에 실패하면 errors가 반환됩니다. */}{errors.exampleRequired && <span>This field is required</span>}<input type="submit" /></form>)}
React 웹 비디오 튜토리얼
이 비디오 튜토리얼은 React Hook Form의 기본 사용법과 개념을 설명합니다.
Register field
React Hook Form의 주요 개념 중 하나는 컴포넌트를 훅에 register
하는 것입니다. 이렇게 하면 해당 컴포넌트의 값이 form validation 및 제출에 사용될 수 있습니다.
참고: 각 field는 register 과정에서 name
이라는 키가 필요
합니다.
import ReactDOM from "react-dom"import { useForm, SubmitHandler } from "react-hook-form"enum GenderEnum {female = "female",male = "male",other = "other",}interface IFormInput {firstName: stringgender: GenderEnum}export default function App() {const { register, handleSubmit } = useForm<IFormInput>()const onSubmit: SubmitHandler<IFormInput> = (data) => console.log(data)return (<form onSubmit={handleSubmit(onSubmit)}><label>First Name</label><input {...register("firstName")} /><label>Gender Selection</label><select {...register("gender")}><option value="female">female</option><option value="male">male</option><option value="other">other</option></select><input type="submit" /></form>)}
Apply validation
React Hook Form은 기존 HTML 표준 form validation을 따름으로써 form validation을 쉽게 만듭니다.
지원되는 validation 규칙 목록:
- required
- min
- max
- minLength
- maxLength
- pattern
- validate
각 규칙에 대한 자세한 내용은 register 섹션에서 확인할 수 있습니다.
import { useForm, SubmitHandler } from "react-hook-form"interface IFormInput {firstName: stringlastName: stringage: number}export default function App() {const { register, handleSubmit } = useForm<IFormInput>()const onSubmit: SubmitHandler<IFormInput> = (data) => console.log(data)return (<form onSubmit={handleSubmit(onSubmit)}><input {...register("firstName", { required: true, maxLength: 20 })} /><input {...register("lastName", { pattern: /^[A-Za-z]+$/i })} /><input type="number" {...register("age", { min: 18, max: 99 })} /><input type="submit" /></form>)}
기존 form 통합
기존 form을 통합하는 것은 간단합니다. 중요한 단계는 컴포넌트의 ref
를 register
하고 관련 속성을 입력에 할당하는 것입니다.
import { Path, useForm, UseFormRegister, SubmitHandler } from "react-hook-form"interface IFormValues {"First Name": stringAge: number}type InputProps = {label: Path<IFormValues>register: UseFormRegister<IFormValues>required: boolean}// 다음 컴포넌트는 기존 Input 컴포넌트의 예시입니다.const Input = ({ label, register, required }: InputProps) => (<><label>{label}</label><input {...register(label, { required })} /></>)// React.forwardRef를 사용하여 ref를 전달할 수도 있습니다.const Select = React.forwardRef<HTMLSelectElement,{ label: string } & ReturnType<UseFormRegister<IFormValues>>>(({ onChange, onBlur, name, label }, ref) => (<><label>{label}</label><select name={name} ref={ref} onChange={onChange} onBlur={onBlur}><option value="20">20</option><option value="30">30</option></select></>))const App = () => {const { register, handleSubmit } = useForm<IFormValues>()const onSubmit: SubmitHandler<IFormValues> = (data) => {alert(JSON.stringify(data))}return (<form onSubmit={handleSubmit(onSubmit)}><Input label="First Name" register={register} required /><Select label="Age" {...register("Age")} /><input type="submit" /></form>)}
UI 라이브러리와 통합
React Hook Form은 외부 UI 컴포넌트 라이브러리와 쉽게 통합할 수 있습니다. 컴포넌트가 입력의 ref
를 노출하지 않는 경우 Controller 컴포넌트를 사용하여 register 과정을 처리할 수 있습니다.
import Select from "react-select"import { useForm, Controller, SubmitHandler } from "react-hook-form"import { Input } from "@material-ui/core"interface IFormInput {firstName: stringlastName: stringiceCreamType: { label: string; value: string }}const App = () => {const { control, handleSubmit } = useForm({defaultValues: {firstName: "",lastName: "",iceCreamType: {},},})const onSubmit: SubmitHandler<IFormInput> = (data) => {console.log(data)}return (<form onSubmit={handleSubmit(onSubmit)}><Controllername="firstName"control={control}render={({ field }) => <Input {...field} />}/><Controllername="iceCreamType"control={control}render={({ field }) => (<Select{...field}options={[{ value: "chocolate", label: "Chocolate" },{ value: "strawberry", label: "Strawberry" },{ value: "vanilla", label: "Vanilla" },]}/>)}/><input type="submit" /></form>)}
제어된 입력 통합
이 라이브러리는 비제어 컴포넌트와 기본 HTML 입력을 사용합니다. 그러나 React-Select, AntD 및 MUI와 같은 외부 제어 컴포넌트와 작업하는 것을 피하기는 어렵습니다. 이를 간단하게 만들기 위해, 우리는 Controller 컴포넌트를 제공하여 register 과정을 간소화하면서도 사용자 정의 register을 사용할 수 있는 자유를 제공합니다.
컴포넌트 API 사용
import { useForm, Controller, SubmitHandler } from "react-hook-form"import { TextField, Checkbox } from "@material-ui/core"interface IFormInputs {TextField: stringMyCheckbox: boolean}function App() {const { handleSubmit, control, reset } = useForm<IFormInputs>({defaultValues: {MyCheckbox: false,},})const onSubmit: SubmitHandler<IFormInputs> = (data) => console.log(data)return (<form onSubmit={handleSubmit(onSubmit)}><Controllername="MyCheckbox"control={control}rules={{ required: true }}render={({ field }) => <Checkbox {...field} />}/><input type="submit" /></form>)}
훅 API 사용
import * as React from "react"import { useForm, useController, UseControllerProps } from "react-hook-form"type FormValues = {FirstName: string}function Input(props: UseControllerProps<FormValues>) {const { field, fieldState } = useController(props)return (<div><input {...field} placeholder={props.name} /><p>{fieldState.isTouched && "Touched"}</p><p>{fieldState.isDirty && "Dirty"}</p><p>{fieldState.invalid ? "invalid" : "valid"}</p></div>)}export default function App() {const { handleSubmit, control } = useForm<FormValues>({defaultValues: {FirstName: "",},mode: "onChange",})const onSubmit = (data: FormValues) => console.log(data)return (<form onSubmit={handleSubmit(onSubmit)}><Input control={control} name="FirstName" rules={{ required: true }} /><input type="submit" /></form>)}
전역 상태와 통합
이 라이브러리는 상태 관리 라이브러리에 의존할 필요가 없지만, 쉽게 통합할 수 있습니다.
import { useForm } from "react-hook-form"import { connect } from "react-redux"import updateAction from "./actions"export default function App(props) {const { register, handleSubmit, setValue } = useForm({defaultValues: {firstName: "",lastName: "",},})// 데이터를 Redux 스토어로 제출const onSubmit = (data) => props.updateAction(data)return (<form onSubmit={handleSubmit(onSubmit)}><input {...register("firstName")} /><input {...register("lastName")} /><input type="submit" /></form>)}// Redux와 컴포넌트를 연결connect(({ firstName, lastName }) => ({ firstName, lastName }),updateAction)(YourForm)
Handle errors
React Hook Form은 form의 오류를 표시하기 위한 errors
객체를 제공합니다. errors
타입은 주어진 validation 제약 조건을 반환합니다. 다음 예제는 필수 validation 규칙을 보여줍니다.
import { useForm } from "react-hook-form"export default function App() {const {register,formState: { errors },handleSubmit,} = useForm()const onSubmit = (data) => console.log(data)return (<form onSubmit={handleSubmit(onSubmit)}><input{...register("firstName", { required: true })}aria-invalid={errors.firstName ? "true" : "false"}/>{errors.firstName?.type === "required" && (<p role="alert">First name is required</p>)}<input{...register("mail", { required: "Email Address is required" })}aria-invalid={errors.mail ? "true" : "false"}/>{errors.mail && <p role="alert">{errors.mail.message}</p>}<input type="submit" /></form>)}
서비스와 통합
React Hook Form을 서비스와 통합하려면, 라이브러리의 내장 제출 처리를 사용할 수 있습니다. <Form />
컴포넌트는 form 데이터를 API 엔드포인트나 다른 서비스에 쉽게 보낼 수 있습니다. Form 컴포넌트에 대해 자세히 알아보기.
import { Form } from "react-hook-form"function App() {const { register, control } = useForm()return (<Formaction="/api/save" // FormData로 POST 요청을 보냅니다.// encType={'application/json'} JSON 객체로 전환할 수도 있습니다.onSuccess={() => {alert("Your application is updated.")}}onError={() => {alert("Submission has failed.")}}control={control}><input {...register("firstName", { required: true })} /><input {...register("lastName", { required: true })} /><button>Submit</button></Form>)}
스키마 validation
우리는 Yup, Zod , Superstruct 및 Joi와 같은 스키마 기반 form validation도 지원합니다. schema
를 useForm의 옵션으로 전달하여 입력 데이터를 스키마에 맞게 validation하고, errors 또는 유효한 결과를 반환합니다.
단계 1: 프로젝트에 Yup을 설치합니다.
npm install @hookform/resolvers yup
단계 2: validation을 위한 스키마를 준비하고 React Hook Form에 입력을 register합니다.
import { useForm } from "react-hook-form"import { yupResolver } from "@hookform/resolvers/yup"import * as yup from "yup"const schema = yup.object({firstName: yup.string().required(),age: yup.number().positive().integer().required(),}).required()export default function App() {const {register,handleSubmit,formState: { errors },} = useForm({resolver: yupResolver(schema),})const onSubmit = (data) => console.log(data)return (<form onSubmit={handleSubmit(onSubmit)}><input {...register("firstName")} /><p>{errors.firstName?.message}</p><input {...register("age")} /><p>{errors.age?.message}</p><input type="submit" /></form>)}
React Native
React Native에서도 동일한 성능 향상 및 개선을 얻을 수 있습니다. 입력 컴포넌트와 통합하려면 Controller
로 감쌀 수 있습니다.
import { Text, View, TextInput, Button, Alert } from "react-native"import { useForm, Controller } from "react-hook-form"export default function App() {const {control,handleSubmit,formState: { errors },} = useForm({defaultValues: {firstName: "",lastName: "",},})const onSubmit = (data) => console.log(data)return (<View><Controllercontrol={control}rules={{required: true,}}render={({ field: { onChange, onBlur, value } }) => (<TextInputplaceholder="First name"onBlur={onBlur}onChangeText={onChange}value={value}/>)}name="firstName"/>{errors.firstName && <Text>This is required.</Text>}<Controllercontrol={control}rules={{maxLength: 100,}}render={({ field: { onChange, onBlur, value } }) => (<TextInputplaceholder="Last name"onBlur={onBlur}onChangeText={onChange}value={value}/>)}name="lastName"/><Button title="Submit" onPress={handleSubmit(onSubmit)} /></View>)}
TypeScript
React Hook Form은 TypeScript
로 빌드되었으며, form 값을 지원하기 위해 FormData
타입을 정의할 수 있습니다.
import * as React from "react"import { useForm } from "react-hook-form"type FormData = {firstName: stringlastName: string}export default function App() {const {register,setValue,handleSubmit,formState: { errors },} = useForm<FormData>()const onSubmit = handleSubmit((data) => console.log(data))// firstName과 lastName은 올바른 타입을 가집니다.return (<form onSubmit={onSubmit}><label>First Name</label><input {...register("firstName")} /><label>Last Name</label><input {...register("lastName")} /><buttontype="button"onClick={() => {setValue("lastName", "luo") // ✅setValue("firstName", true) // ❌: true는 string이 아닙니다.errors.bill // ❌: 속성 bill은 존재하지 않습니다.}}>SetValue</button></form>)}
디자인과 철학
React Hook Form의 디자인과 철학은 사용자와 개발자의 경험에 중점을 둡니다. 라이브러리는 성능을 미세하게 조정하고 접근성을 향상시켜 사용자에게 더 부드러운 상호 작용 경험을 제공하는 것을 목표로 합니다. 성능 향상의 일부는 다음과 같습니다:
- 프록시를 통한 form 상태 구독 모델 도입
- 불필요한 계산 방지
- 필요한 경우에만 컴포넌트 리렌더링 격리
전반적으로, 이는 사용자가 애플리케이션과 상호 작용할 때 사용자 경험을 향상시킵니다. 개발자를 위해, 내장된 validation을 제공하고 HTML 표준과 긴밀하게 일치하여 강력한 validation 방법과 스키마 validation과의 통합을 더욱 확장할 수 있도록 합니다. 또한, TypeScript의 강력한 타입 검사를 통해 개발자에게 초기 빌드 시간 피드백을 제공하여 견고한 form 솔루션을 구축하는 데 도움을 줍니다. 다음 논의에서는 Bill이 일부 아이디어와 디자인 패턴을 소개합니다:
지원해 주셔서 감사합니다
프로젝트에서 React Hook Form이 유용하다고 생각하신다면, 스타를 눌러 지원해 주시길 부탁드립니다.