React react-hook-form Example
react-hook-form을 사용해서 사용자 입력 데이터를 서버로 전송하는 기능 구현하기
설치
yarn add react-hook-formForm 영역
사용자가 입력하는 form 영역은 다음 사진에서 표시한 부분이다.
Readable이라는 interest를 선택하고, tag1, tag2, tag3 태그를 추가한 후, ‘Readable it’ 이라고 적힌 버튼을 클릭하면 서버로 데이터가 전송되어야 한다.
useForm
| useForm is custom hook for managing forms with ease.
우선, useForm 이라는 custom hook으로 default 값들을 지정해주었다.
useForm의 return 값인 methods로 form 데이터를 관리할 수 있다.
type FormValues = {
tags: {
name: string;
}[];
interests: {
name: string;
}[];
selectedInterest: string;
};
const methods = useForm<FormValues>({
defaultValues: {
tags: userData.tags ?? [],
interests: userData.interests ?? [{ name: 'Readable' }],
selectedInterest: userData.selectedInterest ?? 'Readable',
},
});useForm으로 validation도 custom하게 설정할 수 있다. → useForm 공식 문서
useFormContext
Interest 입력 부분과 tag 입력 부분, 두 곳의 데이터를 전송해야하기 때문에 useFormContext를 사용했다.
<FormProvider /> 에 useForm의 return 값인 methods를 넣어주고 <Interests />, <HashTagInput /> 컴포넌트를 감싸주면 Interests와 HashTagInput 컴포넌트에서 useFormContext() hook으로 form 데이터를 공유 할 수 있다.
(React에서 사용하는 Context와 기능, 사용법이 같고, 부모 컴포넌트에서 자식 컴포넌트로 props를 통해서 데이터를 전달하지 않아도 된다는 장점이 있다.)
<FormProvider {...methods}>
<div className="col-start-1 row-start-3 space-y-3 px-4 pb-4">
<Interests />
<HashTagInput />
</div>
</FormProvider>useFieldArray
| Custom hook for working with uncontrolled Field Arrays (dynamic inputs).
react-hook-form에서 지원하는 useFieldArray hook을 사용하면 input을 추가하고, 삭제하는 기능을 정말 편하게 구현 할 수 있다.
우선, useFormContext로 context의 methods를 가져온 다음, useFieldArray에 methods.control과 control 하고 싶은 form 데이터의 name을 넘겨주면 fields 데이터, append, remove 함수등을 사용할 수 있게 된다.
const methods = useFormContext();
const { register, control } = methods;
const { fields, append, remove, insert } = useFieldArray({
name: 'tags',
control,
});fields에는 처음에 useForm을 통해 넣어준 default 값이 들어있다. 따라서 일단 fields 데이터를 화면에 뿌려준다. (태그 자체는 input이 아니기 때문에 input 태그를 hidden으로 넣어주었다.)
input 태그에 methods.register를 이용해서 등록을 해준다.
<ul className="flex flex-wrap mb-4 text-sm font-medium max-h-32 overflow-y-auto">
{fields.map((field, index) => {
return (
<li key={field.id} className="flex mr-2 mb-1">
<Chip backgroundColor="bg-yellow-200" fontColor="text-black">
<input {...register(`tags.${index}.name` as const)} className="hidden" />
<>#{field['name']}</>
</Chip>
<button type="button" className="ml-1" onClick={() => remove(index)}>
<BackspaceIcon className="w-5 h-5 text-gray-300 hover:text-gray-500"></BackspaceIcon>
</button>
</li>
);
})}
</ul>태그를 입력하고 Enter를 치면 태그가 추가 되게끔 구현하고 싶었기 때문에 태그 입력 핸들러는 다음과 같이 작성하였다.
const handleTagAddButtonClick = e => {
const inputTagValue = tagInputRef.current.value;
if (e.key === 'Enter' && inputTagValue) {
append({ name: inputTagValue });
}
};Submit
react-hook-form은 Enter 키를 입력하면 자동으로 submit 동작을 하게끔 구현되어있는 것 같았다.
나는 Enter 키를 입력하면 태그를 추가하고 싶었을 뿐 submit 동작은 원하지 않았기 때문에
input 태그의 type을 button으로 지정해준 뒤 onClick으로 submit 핸들러가 동작하게끔 구현하였다.
// Readable it 버튼
<input
type="button"
onClick={() => onSubmit()}
className="bg-indigo-100 text-indigo-700 text-base font-semibold px-6 py-2 rounded-lg ml-auto disabled:opacity-50"
value="Readable it"
/>useForm의 return 값인 methods.getValues를 사용해서 form 데이터를 가져올 수 있다.
// submit 핸들러
const onSubmit = () => {
const { tags, selectedInterest } = getValues();
submitData({ tags, interest: selectedInterest });
};주의할 점
<FormProvider /> 에 감싸진 <button /> 태그들은 꼭 잊지말고 type을 지정해주어야한다.
예를 들어 type="button"이라고 지정해주지 않으면 엔터를 입력했을 때 제멋대로 submit 동작이 되는 문제가 생긴다.
