react hook form
큰 form 을 가지고 있을 때 좋다.
예를 들어, 누군가 계정을 생성하거나 회원가입을 하고 싶을때,
사용자 이름
사용자 성
아이디
비밀번호
비밀번호 확인
이메일
관심항목
작성하고 각각에 맞는 state를 설정해주고 이벤트 함수를 달아주어야 한다.
여기서 끝이 아니고 form validation까지 진행해야 한다.
validation : 데이터의 타입,조건을 체크하는 과정
예를 들어 1개의 인풋을 검증한다고 가정하면,
form을 submit 할 때 검증을 직접 해줘야 하는데,
inputvalue의 길이가 10보다 작다면
10글자 보다 길어야 합니다, 라고 에러창을 입력해줘야함
에러를 보여주려면 에러스테이트를 만들고 메세지를 작성해야 함.
또 10글자보다 적어지면 메세지를 삭제할 수 있게도 만들어야 함.
useform을 사용하면 이런 반복성을 획기적으로 줄일 수 있다.
useform 내에는 register라는 함수가 있는데
위에 작성해야 하는 모든 것들을 해결해준다.
register 함수를 log 해보면
1개의 문자열과, 3개의 함수로 구성된 객체가 생성되는 것을 알 수 있다.
console.log("toDo");
{name: 'toDo', onChange: ƒ, onBlur: ƒ, ref: ƒ}
onBlur : 인풋 안쪽을 클릭하면 생기는 focus와 반대인 개념
인풋외부를 클릭하였을때의 focus의 흔적
register를 사용하면 , onChange 이밴트와 value , useState를
대체한다.(상태를 만들어줌)
하나의 인풋이라면, useForm은 큰 쓸모가 없을 수 있겠지만,
만들어지는 모든 input의 이름을 추적하여 객체형태로 입력값을 알게해준다.
register는 Input tag의 불안정한 required도 해결해 줄 수 있다.
악의적인 사용자는 개발자 도구에서
required를 삭제하고 submit을 진행할 수 있다.
(HTML TAG를 보호하지 않는 브라우저나 모바일)
그렇기에 javascript로 required를 보호하는 것
<input {...register("email",{required: true})}>
그리고 required가 만족하지 않는 input으로 커서를 자동으로 옮겨준다.
required 뿐만 아니라 input tag가 제공하는 속성들을 기입하여
조건들을 지킬 수 있게 해준다(특별한 함수 작성 없이!)
<input {...register("email",{required: true,minLength: 10, maxLength:20})}>
register에 input tag의 속성을 적을 때, 그 속성의 조건을
만족하지 못할 때 보내고 싶은 메시지를 보내줄수도 있다.
객체 형태로 값과, 보낼 메시지를 입력한다.
<input {...register("email",{required: true,minLength:{ value: 5, message: "write more than 5 word"}})}/>
regular expression validation(정규표현식 검증)
입력한 문자열이 어떤 형식으로 작성되어야 하는지
조건을 제공할 수 있다.
useForm이 제공하는 handleSubmit
handleSubmit은 validation(검증),preventDefault를 담당한다.
handleSubmit을 사용하기 위해서는 하나의 함수를 작성해야 하는데,
react-hook-form이 모든 validation을 다 마쳤을 때만
호출되게 만든다.
handleSubmit은 form tag에 onSubmit프롭스의 함수로 입력한다.
handleSubmit은 2개의 인자를 받는데 첫번쨰는 데이터가 유효할 떄 호출
되는 함수다.(알기 쉽게 onValid로 작명)
두번째 인자는 데이터가 유효하지 않을 떄 호출되는 함수다.(onInvalid로 작명)
두번째 인자는 필수가 아니지만 첫번째 인자는 필수 입력이다.
기존의 onSubmit과 다른점은 작성한 함수를 handleSubmit의 콜백함수로 호출을 해주어야 한다는 것 onSubmit={handleSubmit(onVaild)}
watch 함수
useForm hook은 watch라는 것을 제공하는데
사용자가 form 내 입력값들의 변화를 관찰할 수 있게 해주는함수다.
formState 함수
formState.errors를 통해 어떤 Error인지 알 수 있게 해준다.
Validation 방법
유저에게 입력받은 username같은 경우 API를 활용해서 검사할 수도 있다.
우선 사용자가 적을 수 있게 허용한 다음, 사용자가 적는 도중에
API에 요청을 보내서 사용자명이 이미 존재하는지를 확인할 수도 있다.
만약 사용자명이 이미 존재한다면, 사용자에게 즉시 알려줌.
이런 것들은 react-hook-form이 사용자가 만든 규칙에 따라
검사할 수 있게 해주기 때문에 가능하다.
기존의 html tag를 이용한 조건 뿐만 아니라
사용자가 설정한 조건에 맞는 Error를 발생시키는 방법을 알아야 한다.
상황1. password와 passwordconfirmation이 갖지 않을 경우 에러 발생
1.onValid 함수로 받는 data의 타입을 IForm으로 명시해준다.(Typescript)
const onValid = (data:IForm) =>{};
2.react-use-form에서 setError를 가져와서 함수 내용을 작성한다.(setError는 특정한 에러를 발생시키게 해준다.)
if(data.password !== data.passwordconfirmation){
setError("passwordconfirmation",{message: "wrong password. check password"})
사용자가 form을 제출하면 그 form을 백앤드로 보내주려고 할때
서버가 불안정하거나 접속이 끊긴 상태라면 유저에게 에러를 보여주는게 좋다
하지만 setError에는 정해놓은 IForm 옵션만 나온다.
여기서 개발자는 form인터페이스에 항목을 하나 더 추가하고
(extraError?: string;)
onValid 함수에
setError("extraError",{message: "Server has problem"})을 추가하고
JSX내에 입력한다.
{error?.extraError?.message}
setError를 사용하면서 또 유용한 점은, form에서 고른
input 항목에 강제로 focus하게 만들 수 있다.
이름에 특정 문자를 포함한다면 가입을 허용하지 않는 상황
validate를 사용한다.
validate는 boolean값을 반환한다.
{validate: (value) => !value.includes('nico')]
react-hook-form에서 문자열을 리턴하면, 그건 개발자가 에러 메세지를
리턴하겠다는 뜻이 된다.
validate: (value) => value.includes("%") ? "can not use %" : true}
validate는 하나의 함수 또는 여러 함수가 있는 객체가 될 수 있다.
input에 여러 개의 검사를 사용할 때 사용한다.
validate:{
noPersent: (value) => value.includes("%) ? "can not use %" : true)
noHashtag: (value) => value.includes("#") ? "can not use %" : true)
}
setValue
submit 후 input 글자들을 전부 비워줄 수 있다.
onvalid 함수에 작성한다
setValue("email","");
setValue("password","");
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
interface IForm {
email: string;
passwordconfirmation: string;
password: string;
reservation: string;
birthday: string;
time: string;
extraError?: string;
}
function ToDoList() {
const {
register,
handleSubmit,
formState: { errors },
setError,
} = useForm({
defaultValues: {
email: '@naver.com',
},
});
const onValid = (data: IForm) => {
if (data.password !== data.passwordconfirmation) {
setError('passwordconfirmation', {
message: '비밀번호가 맞지 않습니다.',
});
}
setError('extraError', { message: 'Server has problem' });
};
return (
<div>
<form
style={{ display: 'flex', flexDirection: 'column' }}
onSubmit={handleSubmit(onValid)}
>
<input
{...register('email', {
required: 'email is required',
minLength: { value: 10, message: 'that is too short' },
// pattern: {
// value: /^[A-Za-z0-0._%+-naver.com]$/,
// message: 'only naver domain allowed',
// },
})}
type="text"
placeholder="Write a to do"
/>
<span>{errors?.email?.message}</span>
<input
{...register('password', {
required: 'password is required',
minLength: {
value: 10,
message: 'please white more than 10 word',
},
})}
type="password"
placeholder="Write a password"
/>
<span>{errors?.password?.message}</span>
<input
{...register('passwordconfirmation', {
required: 'who meet you?',
minLength: {
value: 10,
message: 'it is too short',
},
})}
type="password"
placeholder="Write a password confirmation"
/>
<span>{errors?.passwordconfirmation?.message}</span>
<input
{...register('time', {
validate: (value) => (value.includes('%') ? 'can not use %' : true),
})}
type="text"
placeholder="Write a time"
/>
<input
{...register('reservation')}
type="text"
placeholder="Write a reservation"
/>
<input
{...register('birthday', {
minLength: {
value: 4,
message: 'it is too short',
},
})}
type="text"
placeholder="Write a birthday"
/>
<span>{errors?.birthday?.message}</span>
<button>ADD</button>
{/* {errors?.extraError?.message} */}
</form>
</div>
);
}
export default ToDoList;
이메일 정규표현식
[register("email", { required: true, pattern: /^\S+@\S+$/i })]