useForm을 이용해서 주문서 폼을 만들던 도중 한 문제에 직면하게 됐다.
위의 폼에서 체크박스를 체크해야 결제하기 버튼이 초록색으로 바뀌며 활성화되도록 하고 싶었다. useState를 이용해 체크박스의 상태를 컨트롤하려고 했으나 state가 많아지면 리렌더링이 계속된다는 단점이 있어 useForm으로 폼을 만들고 있었기 때문에 useForm 안의 기능들로 해결하고 싶었다.
우선 마크업은 이렇다.
<input type="checkbox" id="check" {...register("checkBox", { required: true })}
/>
<label htmlFor="check"> 주문 내용을 확인하였으며, 정보 제공 등에 동의합니다.</label>
useForm에는 폼의 상태를 알 수 있는 formState
가 있다. 이 안에는 isValid
라는 에러가 있는지 확인해 주는 친구가 있다. isValid
를 사용해 체크박스를 동작시켜 보았다.
const {
register,
formState: { errors, isValid },
handleSubmit,
} = useForm({
mode: "onBlur",
});
mode가 onBlur
인 상태라 바깥 부분을 한 번 눌러 줘야 값이 변경된다는 점과, false만 출력하는 절망적인 결과를 보게 되었다. 알고 보니 isValid
는 렌더링 이후에 유효성 검사를 한다는 문제점이 있다는 것. 그래서 이 함수를 커스텀(?) 해 보기로 했다.
입력되는 값을 실시간으로 감시하기 위해서 watch
함수를 사용한다. watch
함수는 폼에 입력된 값을 실시간으로 체크한다. 그런 다음 isValid
에 담아 콘솔에 찍어 보면 다음과 같은 결과를 얻을 수 있다.
const {
register,
formState: { errors },
handleSubmit,
watch,
} = useForm({
mode: "onBlur",
});
const isValid = watch("checkBox");
체크박스는 초깃값이 null인 상태. 이 상태를 체크 전과 체크 후로 나눠 감시하기 위해 초깃값(체크되지 않은 상태)을 false로, 체크된 상태를 true로 출력할 수 있게 다음처럼 바꿔 주었다.
const {
register,
formState: { errors },
handleSubmit,
watch,
} = useForm({
mode: "onBlur",
defaultValues: { checkBox: false },
});
const isValid = watch("checkBox");
그런 다음 disabled
속성에 !isValid
를 입력해 준다. true 값이 들어갈 때 disabled
속성이 동작되도록 해야 하기 때문이다.
<button disabled={!isValid} onClick={onSubmit}>결제하기</button>
🔗 참고
formState.isValid return true in initial render with mode onChange,onBlur #1512