우리는 사용자의 정보를 받아 실시간으로 미리보기를 제공하기로 하였다.
reacthookform을 사용하여 구현하기로 하였다.
useState의 사용없이 useForm (control, register) 과 useWatch로 실시간 데이터를 받아 폼의 입력 값이 변경될 때마다 미리보기를 띄우는데 용이했다. 간단한 로직 처리로 빠른 처리를 할 수 있는 점이 장점이었다.
npm install react-hook-form
const { register, handleSubmit, usewatch, formState: { errors } } = useForm()
☼ 공식문서 예제
import { useForm, SubmitHandler } from "react-hook-form";
type Inputs = {
example: string,
exampleRequired: 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" 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>
);
}
신부, 신랑 측 성함을 받고 뒤에 미리보기로 실시간으로 연결을 해주어야했다.
컴포넌트를 분리하여 Input.tsx와 Preview.tsx로 나누었고 Input.tsx에서 값을 입력받고 Preview에서 useWatch()를 사용하여 변경되는 부분을 리렌더링하여 볼 수 있도록 만들어주었다.
const MainPhotoInput = () => {
const { register } = useFormContext();
return (
<div className='flex flex-col justify-center items-center '>
<div className='flex flex-col gap-2'>
<h2 className='text-2xl font-bold text-black text-center'>청첩장 대표 사진</h2>
<div className='flex gap-4'>
<label>이름</label>
<input
type='text'
placeholder='좌측'
{...register('mainPhoto_info.leftName')}
className='h-[32px] w-[92px] pl-[8px] py-[9px] border text-[12px] rounded-[8px]'
/>
<input
type='text'
placeholder='♥︎'
{...register('mainPhoto_info.icon')}
className='h-[32px] w-[40px] pl-[8px] py-[9px] border text-[12px] rounded-[8px] placeholder:text-center'
/>
<input
type='text'
placeholder='우측'
{...register('mainPhoto_info.rightName')}
className='h-[32px] w-[92px] pl-[8px] py-[9px] border text-[12px] rounded-[8px]'
/>
</div>
<div className='flex gap-4 '>
<label>글꼴</label>
<div className='grid grid-cols-2 gap-4 '>
{FONTMENU.map((font) => (
<button
key={font.name}
className='p-1 text-black bg-gray-100 hover:bg-primary300 hover:text-white rounded-md font-bold'
>
{font.name}
</button>
))}
</div>
</div>
<div className='flex gap-4'>
<div>사진영역</div>
<textarea
id='IntroduceContent'
placeholder='메인 화면 문구를 작성해주세요.'
className='rounded'
{...register('mainPhotoInfo.introduceContent')}
></textarea>
</div>
</div>
</div>
);
};
export default MainPhotoInput;
const MainPhotoPreView = ({ control }: { control: Control<InvitationFormType> }) => {
const mainPhotoInfo = useWatch({
control,
name: 'mainPhoto_info',
});
return (
<div className='p-4 border rounded-md text-center'>
<h2 className='text-lg font-bold'>청첩장 대표 사진 미리보기</h2>
<div className='flex justify-center items-center gap-2 mt-4'>
<p className='text-xl'>{mainPhotoInfo?.leftName || '좌측 이름'}</p>
<p className='text-xl'>{mainPhotoInfo?.icon || '♥︎'}</p>
<p className='text-xl'>{mainPhotoInfo?.rightName || '우측 이름'}</p>
</div>
</div>
);
};
이전에는 Form을 사용할 때 useState()를 사용하여 상태를 저장하고 불러와야했는데 register로 간편하게 input을 연결하고 key를 통하여 usewatch()로 효율적으로 리렌더링을 불러와 성능적으로도 개선시킬 수 있는 점이 편리하고 좋았다.