react-hook-form 포커스 아웃, 제출 전 유효성 검사하기

수연·2024년 4월 30일

React

목록 보기
1/7
post-thumbnail

에러 메세지 구현

구글 폼 클론 코딩을 하며 다음 기능을 구현해야했다.

필수 질문에 답변을 하지 않은 경우 경고 표시하기

구글 설문 폼 에러 예시사진

그리고 최대한 다음 규칙을 지키려고 했다.

  • react-hook-form 을 사용하고 있으므로 라이브러리에서 제공해주는 상태를 사용하기
  • 포커스 아웃 시, 폼 제출 시 둘 다 유효성 검사하기

react-hook-form 에서 제공해주는 에러 상태 사용하기

react-hook-form 에서 에러 상태를 받아오기 위해 찾아본 결과 두가지 방법이 있었다.

const { formState, getFieldState } = useForm();
  • formState
    form이 관리하는 전체 input의 에러 값을 가져올 수 있다.
  • getFieldState
    name 파라미터를 지정해 특정 input 의 에러 값을 가져올 수 있다.
// form 의 전체 에러를 가져오고 싶을 때
const { errors } = formState;

// 특정 input 태그의 에러를 가져오고 싶을 때 
const { error } = getFieldState(inputName);

나는 특정 input 태그가 가진 에러를 각 카드에서 자체적으로 판별하길 원해서 두번째 방법을 사용했다.

문제점

🤔 그런데 처음엔 필수 질문에 응답을 하지 않아도 아무런 에러도 뜨지 않았다.

console.log(error); // undefined

무엇이 문제인지 살펴보았더니 내가 원하는 유효성 검사를 실시하는 기준과 기본 유효성 검사 기준이 달랐다.

포커스 아웃 시 유효성 검사하기

useForm 의 mode 프로퍼티로 유효성 검사 조정하기

useFormmode 프로퍼티를 지정하면 유효성 검사를 언제 실행할지 알려준다.

const methods = useForm(); // 아무값도 주지 않은 경우 submit 시 유효성 검사

나는 각 카드에서 포커스 아웃이 된 경우, 그리고 폼을 제출하려는 경우, 이렇게 2가지 경우에 유효성 검사를 하고 싶어서 mode 프로퍼티를 onBlur 로 설정했다.

const methods = useForm({ mode: 'onBlur' });

이렇게 설정하면useForm 에 등록해준 input 요소에서 포커스 아웃이 될 때 유효성 검사가 이뤄진다.

required 프로퍼티 설정하기

그리고 에러 메세지를 띄울 조건을 각 컴포넌트마다 설정했다.

// Controller 를 사용하는 경우
import { Controller } from 'react-hook-form';

const Form = (id) => {
	const { control } = useForm(); 	 
  
  	return (
      <Controller
          name={id}
          rules={{
              required: '필수 입력 값입니다.'
          }}
          control={control}
          render={() => <Input />}
      />
	)
}
// register 를 사용하는 경우
import { Controller } from 'react-hook-form';

const Form = (id) => {
	const { register } = useForm(); 	 
  
	return (
    	<input {...register(id, {
      		required: '필수 입력 값입니다.'
      	})} />
    )
}

required 외에도 validate 등의 프로퍼티를 사용해서 각 입력에 맞는 유효성 검사를 해줄 수 있다.

onBlur 메서드 전달하기

위와 같이 에러가 발생하는 기준을 작성했다면 onBlur 메서드를 전달해줘야 한다.

useForm 에서 mode 프로퍼티를 onBlur 로 설정했으므로, 라이브러리에서 제공해주는 onBlur 이벤트를 input 에 전달시켜야만 제대로 동작한다.

register 로 등록한 input 은 반환값에 onBlur 가 포함되어 있으므로 Controller 로 등록한 경우만 onBlur 메서드를 전달해준다.

⚠️ 참고로 나는 Chakra UI 를 사용해서 순수한 Input 이 아닌 경우 (Radio, Checkbox 등) onChange 프로퍼티에 onBlur 를 적용했다.
사용하는 UI 프레임워크가 어떻게 동작하는지에 따라 onBlur 이벤트를 적절한 곳에 적용해줘야 한다.

return (
	<Controller
  		control={control}
		name={id}
		rules={{
      		required: required && '필수 입력 값입니다.'
		}}
        render={({ field: { onChange, value, onBlur } }) => (
        	<TextField
                value={value}
                onChange={onChange}
                onBlur={onBlur}
			/>
     	)}
	/>
)

여기까지 설정해주면 포커스 아웃 시 유효성 검사를 하는 것을 확인할 수 있다.

포커스 아웃 시 에러가 뜨는 모습 gif

폼 제출 시 유효성 검사하기

마지막으로 폼 제출 시에도 유효성 검사를 해야한다. 처음 응답 페이지로 들어오면 아무런 에러도 없는 상태이기 때문이다.

처음 응답 페이지에 들어왔을 때 모습 사진

이 상태에선 error 객체를 조회해봐도 아무런 값이 들어있지 않다. onBlur 이벤트가 발생할 때만 유효성 검사를 하도록 설정했기 때문이다.

const { errors } = formState;
const { error } = getFieldState(id);

console.log(errors); // {}
console.log(error); // undefined

onBlur 와 onSubmit 둘 다의 모드를 한꺼번에 설정할 수 있으면 좋았겠지만... 아쉽게도 멀티 모드(?)는 존재하지 않아서 submit 이벤트 시에는 직접 에러를 트리거 해줘야 한다.

const { trigger } = useForm({ mode: 'onBlur' });

const handleSubmitForm = () => {
	trigger();
}

trigger 는 파라미터로 useForm 에 등록시킨 input 의 name 프로퍼티를 받아 직접 유효성 검사를 트리거하는 메서드다.

Promise 를 반환하기 때문에 올바른 동작을 위해선 비동기처리를 해주는 것을 잊지 말자.

// 전체를 다 트리거하고 싶은 경우
trigger();

// 하나만 트리거하고 싶은 경우
trigger(inputName);

// 여러개를 트리거하고 싶은 경우
trigger([inputName1, inputName2]);

구글 폼은 제출 시 가장 상단에 있는 하나의 카드만을 트리거 시키고 + 포커싱까지 한다.

에러가 발생하는 경우 trigger 의 shouldFocus 프로퍼티로 에러가 발생하는 input 을 포커싱해줄 수 있다.

그런데 내 경우엔... 설계를 잘못해서 외부 UI 프레임워크를 react-hook-form 이 인식하지 못한건지 포커싱이 제대로 되지 않아서 임의로 포커스를 처리했다.

for (const id of cardId) {
	if ((await trigger(id)) === false) {
		const targetCard = document.getElementById(id) as HTMLElement;
		targetCard.scrollIntoView({ behavior: 'smooth', block: 'center' });
      
      	return;
    }
}

이렇게 처리하여 제출 시에도 유효성 검사를 한 뒤 에러가 있는 카드로 포커싱을 해주는 기능을 구현했다.

제출 시 유효성 검사를 해주는 기능 구현 gif

0개의 댓글