RHF를 하면서 겪은 어려움들

리렌더링은 싫은데 검사는 바로바로 하고싶어

내가 원하는 작동 방식은

  1. Input 창에 포커스가 풀릴 때(onBlur) 유효성 검사를 하고
  2. 다시 돌아와 입력이 발생하면 유효성 검사를 하는 것이다.

이를 위해 처음에는 watch를 통해 input value를 트리거 할까 했지만 비제어 컴포넌트를 사용하는 RHF의 리렌더링이 적은 장점이 watch를 사용하게 되면 Input이 제어컴포넌트가 되며 리렌더링이 많아지면서 사라진다.

이를 해결하기 위해 formState에서 isDirtydirtyFields를 이용해보려고했다.

하지만 내가 원하는 대로 동작하지 않았고 이유는 화해 기술블로그 글에서 찾을 수 있었다.

간단하게 정리해보자면

isDirty : 모든 필드 중 하나라도 default value와 다른 사용자 입력이 있다면 true로 반환된다.(전체검사)

dirtyFields : 특정 필드에 default value와 다른 사용자 입력이 있으면 true로 반환된다.(특정 필드검사)

이러한 역할을 하지만 주의할 점이 있다.

단순히 위의 역할을 보았을 때는 dirtyFields가 변경되면 isDirty에도 적용이 될 것이라고 예상이 되지만 이 둘은 싱크가 맞지 않는다.

isDirtytrue로 변경된 이후에는 다시 default value값으로 돌아와도 false로 변경되지 않기 때문이다.

이는 RHF 동작 방식에서 이유를 찾을 수 있는데

  • isDirty는 필드의 값을 깊은 비교를 통해 나오는 상태값
  • dirtyFields는 필드의 변경이 있었는지를 나타내는 상태값

이라고 볼 수 있다.

RHF에서 필드의 값이 변경되면 내부적으로 updateTouchAndDirty함수를 실행해서 isDirty와 dirtyFields를 결정하는데 dirtyFields 에서는 isBlurEvent와 shouldDirty라는 option에 의해서 dirtyFields에 필드를 넣을지 말지 결정할 수 있다는 차이점이 있다.

이를 통해, setValue의 option으로 shouldTouch 또는 shouldDirty를 true로 해줘야 isDirtydirtyFields의 싱크를 맞춰줄 수 있다.

하지만…

더 편한 방법이 있었는데 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를 조금 더 자유롭게 사용할 수 있게 되어서 기분이 좋다ㅎㅎ


참고

화해 기술블로그 isDirty, dirtyFields차이

RHF mode

RHF trigger

카테고리:

업데이트:

댓글남기기