멘토링을 진행하던 중 멘토님이 제어/비제어 컴포넌트에 대한 얘기와 react-hook-form
에 대해 간략하게 말씀해주셨는데, 검색을 해보니 현재 form의 input value를 state로 관리하는 것보다 더 편리하게 사용할 수 있을 것 같아서 포스팅으로 정리해두고자 한다!
기존에 사용하던 방식은 state를 통해 input의 값을 제어하고, 모든 state 변경은 핸들러 함수를 통해 변경했다.
react-hook-form
은 비제어 컴포넌트 방식으로 register
함수를 통해 input 요소에 ref를 연결하고, 필요한 이벤트 리스너를 추가한다.
비제어 컴포넌트의 장점을 살리면서도, 일반적인 비제어 컴포넌트의 단점들을 라이브러리 차원에서 해결하여 효율적으로 form 을 관리할 수 있도록 해주는 라이브러리이다.
$ npm install react-hook-form
$ yarn add react-hook-form
email
과 password
2개의 input 으로 이루어진 form 컴포넌트
각 input의 state를 만들고 error를 관리할 state도 만들고
각 input의 value를 변경할 핸들러 함수도 만들고 기능은 잘 동작하지만 코드가 길어지며 모든 값이 state 로 연결되어 있어 하나의 값이 변할때 마다 여러개의 자식 컴포넌트 들에서 무수히 많은 리렌더링이 발생하게 된다.
만약 input이 1000개가 된다면?
1000개의 input을 관리할 state가 1000개가 될 것이고 한 글자씩만 입력해도 1000번의 리렌더링이 발생하게 되고 모든 데이터를 입력할 경우에 무수히 많은 리렌더링이 발생할 것이다.
useForm()
을 사용하게 되면 register()
와 handleSubmit()
함수를 통해 관리할 수 있다.
register
를 이용하여 각 input을 등록할 수 있고, handleSubmit
의 인자로 submit 이벤트를 처리할 핸들러 함수를 넘겨 form 요소에서 발생하는 submit 이벤트를 처리할 수 있다.
현재 submit 이벤트를 핸들링 하기 위해 alert 창에 현재 data 들을 json 문자열로 출력해주는 함수를 넘겨줬다.
해당 코드를 실행해보면 폼을 입력하고 제출할 경우 입력 데이터가 정상적으로 출력되는 것을 볼 수 있다.
react-hook-form 을 사용할 때 configuration 옵션을 설정할 수 있다. 다양한 옵션이 있는데 mode
와 defaultValues
를 많이 활용한다.
// 예시
const {
register,
handleSubmit,
} = useForm<IForm>({
mode: "onSubmit",
defaultValues: {
occupation: "student",
name: "",
pwd: "",
email: "",
},
});
validation 전략을 설정하기 위한 옵션으로
설정하지 않을 경우 default 옵션은 onSubmit
이다.
onSubmit
submit 이벤트가 발생하면 유효성 검증
onBlur
blur 이벤트가 발생하면 유효성 검증
onChange
각 input에 대한 change 이벤트가 발생할때마다 유효성 검증, 여러 번의 리렌더링이 발생해 성능을 저하시킬 수 있음
onTouched
첫 blur 이벤트가 발생한 이후에 모든 change 이벤트가 발생할때 마다 유효성 검증
all
모든 blur & change 이벤트가 발생할 때마다 유효성 검증
form 에 기본 값을 제공하는 옵션
react-hook-form 을 사용할 때 기본값을 제공하지 않는 경우 input 의 초기값은 undefined 이 된다.
register 함수의 첫 번째 인자로는 input의 name을 준다.
해당 필드를 다루게 될 key 값으로써 반드시 들어가야 하는 값이다.
두 번째 인자로는 options 객체가 들어가는데 유효성 검사를 위한 프로퍼티들이 들어갈 수 있다.
(required, min, max, minLength, maxLength, pattern 등)
유효성 검사를 위해 프로퍼티에 value 만 주거나 value, message 로 구성된 객체를 줘서 해당 에러에 대한 구체적인 메세지를 제공하게끔 할 수 있다.
register("name", {required: true, minLength: 10});
register("name", {required: "해당 필드는 필수입니다.", minLength: {
value: 3,
message: "3글자 이상 입력해주세요."
}
});
form에 대한 다양한 상태를 담고 있는 객체
https://react-hook-form.com/docs/useform/formstate
isSubmitting
현재 양식을 제출 중인 경우에 true / 그렇지 않은 경우에 false
isSubmitted
양식이 제출된 후에 true로 설정 메서드가 호출될 때까지 유지된다
isSubmitSuccessful
런타임 오류 없이 양식이 성공적으로 제출되었음을 나타냄
submitCount
form이 제출된 횟수
errors
에러에 관한 내용을 담고 있는 객체로. 오류 메시지를 쉽게 검색하기 위한 ErrorMessage도 갖고 있음
watch
는 지정된 입력 필드의 값 변화를 감지하는 함수로
필드 값이 변경될 때마다 해당 컴포넌트를 리렌더링한다.
interface IFormInputs {
name: string
showAge: boolean
age: number
}
const {
register,
watch,
} = useForm<IFormInputs>()
const watchShowAge = watch("showAge", false)
// 두번째 인자로 기본값을 제공
const watchAllFields = watch()
// 인자를 넣지 않으면 모든 input을 감시
const watchFields = watch(["showAge", "age"])
// 이름으로 여러 필드를 타겟으로 지정할 수 있음
submit 이벤트가 발생했을 때 이벤트를 처리하고 폼 데이터의 유효성을 검사하는 함수이다. 유효성 검사를 통과한 데이터를 콜백 함수에 전달한다.
const { register, handleSubmit } = useForm();
const onSubmit = (data) => {
// data -> 유효성 검사를 통과한 데이터가 전달됨
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
...
</form>
폼 제출이 실패했을 경우를 대비해 폼 제출 실패 핸들러 함수를 두번째 인자로 넘겨줄 수 있다.
const onSubmit = (data) => {
// 성공 시 처리
};
const onError = (errors) => {
console.log(errors);
};
<form onSubmit={handleSubmit(onSubmit, onError)}>
복잡한 상태 관리 로직 없이 구현할 수 있으니 state를 최소화해서 불필요한 리렌더링을 방지할 수 있고 폼을 관리하는 코드를 줄일 수 있다는 장점이 있는 것 같다. 또한 유효성 검사 기능을 제공하기 때문에 유효성 검사 로직을 쉽게 구현할 수 있다는 점이 좋았다. 나중에 프로젝트 할 때 꼭 써보고 싶다