미래의 나에게 보내는 RHF사용법
RHF를 하면서 겪은 어려움들
리렌더링은 싫은데 검사는 바로바로 하고싶어
내가 원하는 작동 방식은
- Input 창에 포커스가 풀릴 때(onBlur) 유효성 검사를 하고
- 다시 돌아와 입력이 발생하면 유효성 검사를 하는 것이다.
이를 위해 처음에는 watch를 통해 input value를 트리거 할까 했지만 비제어 컴포넌트를 사용하는 RHF의 리렌더링이 적은 장점이 watch를 사용하게 되면 Input이 제어컴포넌트가 되며 리렌더링이 많아지면서 사라진다.
이를 해결하기 위해 formState
에서 isDirty
와 dirtyFields
를 이용해보려고했다.
하지만 내가 원하는 대로 동작하지 않았고 이유는 화해 기술블로그 글에서 찾을 수 있었다.
간단하게 정리해보자면
isDirty : 모든 필드 중 하나라도
default value
와 다른 사용자 입력이 있다면true
로 반환된다.(전체검사)
dirtyFields : 특정 필드에
default value
와 다른 사용자 입력이 있으면true
로 반환된다.(특정 필드검사)
이러한 역할을 하지만 주의할 점이 있다.
단순히 위의 역할을 보았을 때는 dirtyFields
가 변경되면 isDirty
에도 적용이 될 것이라고 예상이 되지만 이 둘은 싱크가 맞지 않는다.
isDirty
가 true
로 변경된 이후에는 다시 default value
값으로 돌아와도 false
로 변경되지 않기 때문이다.
이는 RHF 동작 방식에서 이유를 찾을 수 있는데
isDirty
는 필드의 값을 깊은 비교를 통해 나오는 상태값dirtyFields
는 필드의 변경이 있었는지를 나타내는 상태값
이라고 볼 수 있다.
RHF에서 필드의 값이 변경되면 내부적으로 updateTouchAndDirty
함수를 실행해서 isDirty
와 dirtyFields
를 결정하는데 dirtyFields
에서는 isBlurEvent
와 shouldDirty
라는 option에 의해서 dirtyFields
에 필드를 넣을지 말지 결정할 수 있다는 차이점이 있다.
이를 통해, setValue
의 option으로 shouldTouch
또는 shouldDirty
를 true로 해줘야 isDirty
와 dirtyFields
의 싱크를 맞춰줄 수 있다.
하지만…
더 편한 방법이 있었는데 useForm에서는 내가 원하는 mode를 제공하고 있었다!!
RHF에서 제공하는 가능은 5가지가 있다.
타입 | 설명 | |
---|---|---|
onSubmit(기본값) | string | sumbit 시 유효성 검사가 실행되며 이후 input은 onChange 이벤트가 발생시 유효성 검사를 재실시 한다 |
onBlur | string | 포커스 아웃 시 유효성 검사가 실행된다. |
onChange | string | 각 입력값이 변경될 시 유효성 검사를 실시 한다 |
(리렌더링이 잦아질 수 있어 성능에 좋지 않다.) | ||
onTouched | string | onBlur와 onChange를 합친 것과 같은 효과로 |
all | string | 위의 모든 이벤트에서 유효성 검사를 실행 |
이러한 mode 제공값들이 있었고 defalutValue에 등록한 값은 원하는 기능대로 동작한다.
초기 필드가 없으면 mode가 적용 안되는데..?우짜지??
하지만 useForm 실행시 필드가 없는경우에는 작동하지 않는 문제가 있었고 새로운 방법을 찾아야 했다.
역시나…!! 유효성 검사를 수동으로 실행 시킬 수 있는 trigger
라는 기능이 있었다!
이를 통해 내가 원하는 기능을 위해서는 onTouched
mode로 변경 후 초기에 필드가 없는 값의 경우 input값이 변경되었을 때 trigger
를 통해 유효성 검사를 해주는 방법으로 구현하였다.
완성된코드
...
// useForm option
const {
register,
handleSubmit,
setValue,
getValues,
formState: { errors, isValid },
watch,
trigger,
} = useForm<MGCCreateForm>({
mode: 'onTouched',
defaultValues: {
title: '',
location: '',
maxParticipants: 10,
},
});
...
// 초기 필드가 없는 경우
...
const handleMGCDate = (field: keyof MGCCreateForm, selectedDay: Date) => {
setValue(field, selectedDay);
trigger(field);
};
...
<MGCDate
title="* 모각코 날짜"
onSelectedDay={(selectedDay) => handleMGCDate('date', selectedDay)}
errorMessages={errors.date?.message}
/>
...
RHF를 조금 더 자유롭게 사용할 수 있게 되어서 기분이 좋다ㅎㅎ
댓글남기기