React Hook Form
React Hook Form을 사용하는 이유
- validation을 쉽게 구현할 수 있다.
- error를 쉽게 설정하고 초기화 할 수 있다.
- event를 하나하나 신경쓰지 않아도 된다.
- 코드의 양을 줄일 수 있다. (반복되는 코드를 줄일 수 있다.)
Form
우선 비교를 위해서 React hook form 없이 기본 react만으로 코드를 짜본다.
기본적으로 username, pw, email을 입력하는 input을 만들고, 각각 onChange함수를 만들어주고 submit을 하게 한다.
//기본 form으로 짠 코드
export default function Forms() {
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const onUsernameChanege = (e: React.SyntheticEvent<HTMLInputElement>) => {
const {
currentTarget: { value },
} = e;
setUsername(value);
};
const onEmailChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
const {
currentTarget: { value },
} = e;
setEmail(value);
};
const onPasswordChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
const {
currentTarget: { value },
} = e;
setPassword(value);
};
const onSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();
console.log(password, email, username);
};
return (
<form onSubmit={onSubmit}>
<input
value={username}
onChange={onUsernameChanege}
type="text"
placeholder="Username"
required
minLength={5}
/>
<input
value={email}
onChange={onEmailChange}
type="email"
placeholder="Email"
required
/>
<input
value={password}
onChange={onPasswordChange}
type="password"
placeholder="Password"
required
/>
<input type="submit" value="Create Account" />
</form>
);
}
추가적으로 유효성검사를 위한 코드를 고려해야하는데,
const [formErrors, setFormErrors] = useState('');
const [emailError, setEmailError] = useState('');
const onSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();
if (username === '' || email === '' || password === '') {
setFormErrors('All fields are required');
}
if (!email.includes('@')) {
setEmailError('~~');
}
};
위의 코드에서는 input이 비어있을 경우와 email 입력시 @를 포함하지 않은 경우만 추가해 보았다.
이외에도 비밀번호의 길이, 특수문자 추가 등 여러가지의 유효성검사를 하려면 수많은 state를 만들어야 하며,
error후 수정이 되면 error msg를 사라지게 한다는 등의 form에 대한 user의 좋은 경험을 위해 고려해야할 사항들이 매우 많다.
이러한 form코드를 좀 더 수월하게 짜기위하여 React hook form을 사용해보겠다.
React Hook Form - useForm
모든것은 useForm으로 구현이 된다.
form에 기본 input을 만들고 useForm을 생성한다.
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
export default function Forms() {
const {} = useForm();
return (
<form>
<input type="text" placeholder="Username" required minLength={5} />
<input type="email" placeholder="Email" required />
<input type="password" placeholder="Password" required />
<input type="submit" value="Create Account" />
</form>
);
}
🔻 register
: input을 state에 연결한다.
{ ...register(inputName) }
( 기존에 state를 만들고, event listener를 등록하고, value를 input에 넣어주는 과정과 동일 )
//동일한 역할 value={username},onChange={onChange}
export default function Forms() {
const { register } = useForm();
<form>
<input
{...register('username')}
type="text"
placeholder="Username"
required
minLength={5}
></input>
register는 object를 반환하고, 그 안에는 name / onBlur / onChange / ref 4개가 있다.
객체를 받아서 객체의 내부에 있는 속성들을 전부 가져다가 태그의 속성으로 넣어준다.
🔻 watch
: input값을 실시간으로 인식할 수 있다.
( event.target.value와 동일 )
export default function Forms() {
const {watch} = useForm
console.log(watch('email'));
🔻 Validation
- input태그에 required를 넣었을 경우 user가 html에서 required를 삭제하고 올바르지않은 값을 보낼 수 있다.
혹은, html required 속성(or 다른 속성들)을 지원하지 않는 브라우저일 수도 있다.
→ React hook form은 이러한 위험들을 방지할 수 있다.
📌 지정하기를 원하는 속성을 register 다음에 객체로 넣어준다.
<input
{...(register('email'),
{
required: true,
})}
type="email"
placeholder="Email"
/>
- min/max 숫자입력시 최소 최대 숫자 제한
- min/maxLength 텍스트입력 길이 제한
- pattern 정규식으로 입력값 필드 검증 시 사용 (이메일 등)
📌 handleSubmit
: event.PreventDefault 역할 (보다는 좀 더 강력)
두 개의 인자를 받는 함수 ( onValid, onInvalid ), 1개는 필수
→ 첫번째는 form이 유효할 때만 실행되는 함수 onValid
두번째는 유효하지 않을 때 실행되는 onInvalid
export default function Forms() {
// useForm에서 handleSubmit을 불러온다.
const { register, handleSubmit } = useForm();
// 유효성통과하면 console에 hi를 출력
const onValid = () => {
console.log('hi');
};
return (
// 유효성검사에 통과하면 onvalid함수를 곧바로 실행한다
<form onSubmit={handleSubmit(onValid)}>
...
onInvalid
: 유효성검사에 통과되지않을 시 error 메세지 전달하도록 지정
const onInvalid = (errors: FieldErrors) => {
console.log(errors);
};
// Form의 두번째 인자로 onInvalid
<form onSubmit={handleSubmit(onValid, onInvalid)}>
<input
{...register('username', {
required: 'Username is required',
minLength: {
//다섯글자 넘지 않을 시 알릴 메세지
message: 'Username은 5자 이상으로 입력해야 합니다.',
//최소 5자이상 입력
value: 5,
},
})}
type="text"
placeholder="Username"
required
minLength={5}
/>
username에 4글자 입력시 console에 type: 'minLength'
5글자 입력하여 유효성 만족했을 시 username은 console에 뜨지않음!
🔻 Errors
📌 에러를 UI에 표시하기
ex) gmail 사용제한
<input
{...register('email', {
required: 'Email is required',
validate: {
notGmail: (value) =>
!value.includes('@gmail.com') || 'gmail은 사용하실 수 없습니다.',
},
})}
type="email"
placeholder="Email"
/>
{errors.email?.message}
📌 formState
mode
: [ all, onBlur, onChange, onSubmit, onTouched ]
onSubmit : 기본옵션, submit버튼 누르면 validation이 일어남
onBlur : input의 바깥쪽을 클릭했을 때 발생
onChange : input이 변할때마다 매번 validation (ex. ID가 사용중인지 입력시에 백엔드 fetch)
export default function Forms() {
const {
formState: { errors },
} = useForm<LoginForm>({
mode: 'onBlur',
});
→ Tailwind로 input의 state에 따라 에러 스타일 커스텀
<input
...
//email error메세지 발생 시 input border를 red로
className={`${Boolean(errors.email?.message) ? 'border-red-100' : ''}`}
/>
📌 setError
setError('지정input', { message : '메세지내용'}
{ errors.지정input?.message }
export default function Forms() {
const {setError} = useForm<LoginForm>();
const onValid = (data: LoginForm) => {
setError('username', { message: '유저의 이름을 입력하세요.' });
};
return (
<form onSubmit={handleSubmit(onValid, onInvalid)}>
<input
{...register('username', {
required: 'Username is required',
minLength: {
message: 'Username은 5자 이상으로 입력해야 합니다.',
value: 5,
},
})}
type="text"
placeholder="Username"
/>
{errors.username?.message}
📌 reset, resetField
: reset은 전체 input을 reset, resetField는 지정한 input을 reset.
export default function Forms() {
const {
reset,
resetField,
} = useForm<LoginForm>({
mode: 'onChange',
});
const onValid = (data: LoginForm) => {
reset();
resetField('email');
};
🔻 참고사항
> Input을 컴포넌트로 만들어놓고 사용한다면
<Input
register={register('email')}
name="email"
label="Email address"
type="email"
required
/>
export default function Input({
label,
name,
kind = 'text',
register
...rest
}: InputProps) {
return (
<input id={name} {...register} {...rest} />
'Archive' 카테고리의 다른 글
[TIL] React Native - M1 setting (Rosetta 없이) (0) | 2022.05.23 |
---|---|
[GIT] git 기본사용 reset / revert / merge / rebase (0) | 2022.05.15 |
[TS] TypeScript Basic (0) | 2022.04.28 |
[TIL] GraphQL & Apollo (0) | 2022.04.24 |
[TIL220410] PlanetScale (serverless DB platform) (0) | 2022.04.10 |