직접 로직을 구현하기에는 validation,form fields, error 등 고려해야할 부분이 너무 많고, 직접 작성할 경우 코드가 과도하게 늘어난다.
또한, state에 기반해 form을 구성(제어 컴포넌트로)할 경우, 불필요한 렌더링이 과도하게 발생할 수 있다.
물론 colocation과 Ref를 활용해 이를 직접 구현할 수도 있지만, 이를 직접 구현하기에는 데이터가 여러군데로 분산되어 유지 및 관리의 난이도가 너무 높아지고 굳이 기존 라이브러리를 사용하지 않을 이유가 없기 때문에 기존 라이브러리를 도입하기로 하였다.
Features | React Hook Form | Formik | Redux Form |
---|---|---|---|
Component | uncontrolled & controlled | controlled | controlled |
Rendering | minimum re-render | re-render according to local state changes which means as you type in the input. | re-render according to state management lib (Redux) changes which means as you type in the input. |
API | Hooks | Component (RenderProps, Form, Field) + Hooks | Component (RenderProps, Form, Field) |
Package size | Small react-hook-form@7.0.0 8KB | Medium formik@2.1.4 15KB | Large redux-form@8.3.6 26.4KB |
Validation | Built-in, Yup, Zod, Joi, Superstruct and build your own. | Build yourself or Yup | Build yourself or Plugins |
Learning curve | Low to Medium | Medium | Medium |
No. of mounts | Only requires mounting the form | It mounts the form, some additional components, and uses a special component for fields | It mounts the form, some additional components, and uses a special component for fields |
No. of committing changes | 1 | 6 | 17 |
Total mounting times | 1800ms | 2070ms | 2380ms |
성능면에서 react-hook-form이 가장 뛰어나다.
또한 비제어 방식으로 form을 관리할 수 있다는 장점이 있는 React-hook-form을 사용하기로 하였다.
import { useForm } from "react-hook-form";
export default function App() {
const { register, handleSubmit, watch, formState: { errors } } = useForm();
const onSubmit = data => console.log(data);
console.log(watch("example")); // watch input value by passing the name of it
return (
/* "handleSubmit" will validate your inputs before invoking "onSubmit" */
<form onSubmit={handleSubmit(onSubmit)}>
{/* register your input into the hook by invoking the "register" function */}
<input defaultValue="test" {...register("example")} />
{/* include validation with required or other standard HTML validation rules */}
<input {...register("exampleRequired", { required: true })} />
{/* errors will return when field validation fails */}
{errors.exampleRequired && <span>This field is required</span>}
<input type="submit" />
</form>
);
}
Register를 사용하면 5줄의 코드를 다음과 같이 줄일 수 있다.
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')} />
function FieldArray() {
const { control, register } = useForm();
const { fields, append, prepend, remove, swap, move, insert } = useFieldArray({
control, // control props comes from useForm (optional: if you are using FormContext)
name: "test", // unique name for your Field Array
});
return (
{fields.map((field, index) => (
<input
key={field.id} // important to include key with field's id
{...register(`test.${index}.value`)}
/>
))}
);
}
다음과 같이 간편하게 regex 등을 활용한 검증이 가능하다.
<input name="firstName"
ref={register({ required: true, maxLength: 20 })} />
<input name="lastName"
ref={register({ pattern: /^[A-Za-z]+$/i })} />
<input name="age" type="number"
ref={register({ min: 18, max: 99 })} />
중첩된 컴포넌트 구조에서 Provider를 통해 context형식으로 method를 넘겨줄 수 있다.
import React from "react"
import { useForm, FormProvider, useFormContext } from "react-hook-form"
export default function App() {
const methods = useForm()
const onSubmit = (data) => console.log(data)
return (
<FormProvider {...methods}>
// pass all methods into the context
<form onSubmit={methods.handleSubmit(onSubmit)}>
<NestedInput />
<input type="submit" />
</form>
</FormProvider>
)
}
function NestedInput() {
const { register } = useFormContext() // retrieve all hook methods
return <input {...register("test")} />
}
react-hook-form은 dev tool을 제공한다.
form의 데이터 변화를 콘솔에 찍지 않고도 GUI로 확인할 수 있음.
https://tech.inflab.com/202207-rallit-form-refactoring/react-hook-form/