react-hook-form 을 이용해 2차원 이상의 배열을 다뤄야했다.
다행히 폼 라이브러리의 문서에 잘 정리되어 있어서 어렵지 않게 구현하였다. 핵심은 useFieldArray
이다.
예제 코드를 위해 최대한 간단하게 구현해보았다.
import { useForm, useFieldArray, UseFormReturn } from 'react-hook-form';
interface IFormProps {
files: Array<IFile>;
}
interface IFile {
url: string;
images: Array<IImage>;
}
interface IImage {
url: string;
altText: string;
}
const MainForm: React.FC = () => {
const form = useForm<IFormProps>();
const { register, handleSubmit, control } = form;
const { fields, append, remove } = useFieldArray({
control,
name: 'files',
});
const onSubmit = (data: IFormProps) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
{fields.map((item, index) => {
return (
<div key={index}>
<input type="text" {...register(`files.${index}.url`)} />
{/* Nested form */}
<ImagesForm fileIndex={index} form={form} />
<button type="button" onClick={() => remove(index)}>
파일 삭제
</button>
</div>
);
})}
<button type="button" onClick={() => append({ url: '', images: [] })}>
파일 추가
</button>
</form>
);
};
const ImagesForm: React.FC<{ fileIndex: number; form: UseFormReturn<IFormProps> }> = ({
fileIndex,
form: { register, control },
}) => {
const { fields, append, remove } = useFieldArray({
control,
name: `files.${fileIndex}.images`,
});
return (
<div>
{fields.map((item, index) => {
return (
<div key={index}>
<input type="text" {...register(`files.${fileIndex}.images.${index}.url`)} />
<input type="text" {...register(`files.${fileIndex}.images.${index}.altText`)} />
<button type="button" onClick={() => remove(index)}>
이미지 삭제
</button>
</div>
);
})}
<button type="button" onClick={() => append({ url: '', altText: '' })}>
이미지 추가
</button>
</div>
);
};
export default MainForm;
참고: https://react-hook-form.com/docs/usefieldarray
왠지 모르겠지만 공식 문서에는 Nested Form 예제에 타입스크립트 코드가 없다 (...)
좋은 글 감사합니다!