다중 페이지 회원가입 폼을 한번에 관리하기 위해 react-hook-form을 이용 중이다.
인풋에 입력한 값은 {...register('name')}
와 같이 필드를 연결해서 넘겼다. handleSubmit
함수를 사용하여 폼 제출을 처리할 때, React Hook Form은 등록된 필드의 값들을 모아서 data 객체로 전달해준다. 직접 데이터를 처리할 경우 이런 기능을 사용할 수 없다.
<Input
type='text'
{...register('name')}
placeholder='이름을 입력해주세요.'
/>
예시로 이렇게 하면 인풋에 입력한 텍스트가 'name' 필드값으로 넘어간다.
const [joinData, setJoinData] = useState({
username: '',
password: '',
name: '',
sex: '',
nickname: '',
address: '',
birth: '',
primaryPositions: '',
});
...
const updateData = (data: any) => {
setJoinData((prevData) => ({ ...prevData, ...data }));
};
참고로 각 페이지에 updateData 함수를 props로 내려줘서 인풋에 입력된 값들을 업데이트 해주고 있었다.
문제는 생년월일 필드에서 발생했다. birth라는 하나의 필드 값을 넘겨야 하는데, 입력값을 한번에 받는 게 아니라 년/월/일 3개가 필요했다. 인풋 3개에 각 값을 받아와서 '240704' 같은 형태로 조합해 다음과 같이 birth로 넘겨주었다. 값이 다음 페이지로 잘 넘어갔지만, 실제로 최종 폼 제출을 할 때는 birth 값이 들어오고 있지 않다는 것을 확인했다.
import { useFormContext } from 'react-hook-form';
const {
register,
handleSubmit,
setValue,
} = useFormContext();
...
const nextHandler = (data: any) => {
const birth = `${selectedYear}${selectedMonth}${selectedDay}`;
data.birth = birth;
updateData(data);
onNext();
};
nextHandler 함수에서 data.birth를 직접적으로 설정하고 updateData 함수를 통해 전달하는 방식은 React Hook Form과의 연동을 무시하고 있기 때문에 데이터가 정상적으로 넘어가지 않은 것이었다.
다중 값을 하나의 필드 값으로 넘겨주고 싶다면, setValue를 사용하여 React Hook Form에 값을 설정할 수 있다.
const nextHandler = handleSubmit((data: any) => {
const birth = `${selectedYear}${selectedMonth}${selectedDay}`;
setValue('birth', birth);
updateData(data);
onNext();
});
이렇게하니 최종 제출시 birth 값이 잘 들어온다!
그러나
이게 끝이 아니다.
필드 값을 수정할 때마다 업데이트 해주어야 하기 때문!
useEffect(() => {
if (selectedYear && selectedMonth && selectedDay) {
const birth = `${selectedYear}${selectedMonth}${selectedDay}`;
setValue('birth', birth);
}
}, [selectedYear, selectedMonth, selectedDay, setValue]);
...
const nextHandler = (data: any) => {
updateData(data);
onNext();
};
useEffect를 사용해 각 필드의 상태(selectedYear, selectedMonth, selectedDay)가 변경될 때마다 setValue를 통해 React Hook Form의 필드 값을 업데이트해주었다. 이러한 방식으로 React Hook Form의 관리를 유지하면서도 복잡한 상태 관리를 간소화할 수 있다. 게다가 nextHandler와도 분리해서 더욱 깔끔한 코드가 되었다.
react-hook-form을 좀 더 이해하게 되었던 시간이다.
useFormContext는 React Hook Form 라이브러리에서 제공하는 훅으로, FormContext를 통해 하위 컴포넌트에서 폼 상태와 메서드에 접근할 수 있게 해준다.
처음에는 각 인풋의 값을 일일히 제어하고 있었는데, useFormContext의 watch
와 setValue
를 활용하면서 코드가 깔끔해졌다.
setValue
: 폼 필드의 값을 설정할 수 있게 해주는 메서드. 특정 필드의 값을 업데이트해야 할 때 사용한다.
watch
: 폼 필드의 값을 관찰하고 값이 변경될 때마다 업데이트된 값을 반환하는 메서드. 폼 필드의 현재 상태를 실시간으로 추적할 수 있다.
const { register, handleSubmit, setValue, watch } = useFormContext();
...
const username = watch('username') as string;
...
const nextHandler = (data: any) => {
updateData(data);
onNext();
};
...
<Input
type='email'
{...register('username')}
name='username'
value={username}
onChange={(e) => setValue('username', e.target.value)}
placeholder='이메일을 입력해주세요.'
/>
<NextButton
type='submit'
onClick={handleSubmit(nextHandler)}
>
다음
</NextButton>
이렇게 하면 인풋에 입력된 'username' 값이 실시간으로 추적되고, 'username'라는 필드 값으로 넘어가서 제출까지 완료!