React - Hook - Form
install react-hook-form
우리가 React에서 Form을 다룰때 보통
import { useState } from "react";
function ToDoList() {
const [toDo, setTodo] = useState("");
const onChange = (event: React.FormEvent<HTMLInputElement>) => {
const {
currentTarget: { value },
} = event;
setTodo(value);
};
const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
console.log(toDo);
};
return (
<div>
<form onSubmit={onSubmit}>
<input onChange={onChange} value={toDo} placeholder="Write a to do" />
<button>add</button>
</form>
</div>
);
}
export default ToDoList;
이런 번거로운 과정을 거쳐야하는거 기억나는가?
근데 만약 input이 여러개 필요하다면?
저거를 몇개를 쳐야한다. useState가 엄청나게 늘어난다.
이렇게 코드를 짤수는 없다.
그래서 사용하는게 React-hook-form이다.
React-hook-form은 사용하기 쉬운 유효성 검사를 통해 성능이 뛰어나고 유연하며 확장 가능한 form이다.
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
input {...register('lastName', { required: true })}
register: (name: string, RegisterOptions?) => ({ onChange, onBlur, name, ref })
이 메서드를 사용하면 onChange 이벤트 핸들러도 props도 setState도 필요없다.
input을 등록하거나 element를 선택하고 유효성 검사 규칙을 React Hook Form에 적용할 수 있다.
유효성 검사 규칙은 모두 HTML 표준을 기반으로 하며 사용자 지정 유효성 검사 방법도 허용한다.
watch: (names?: string | string[] | (data, options) => void) => unknown
form의 입력값들의 변화를 관찰 할 수 있게 해주는 함수다.
이 메서드는 지정된 input을 감시하고 해당 값을 반환한다. input 값을 렌더링하고 조건에 따라 무엇을 렌더링할지 결정하는 데 유용하다.
input {...register('lastName', { required: true }
input에 사용시 register 함수가 반환하는 객체를 가져다가 props로 준다.
function ToDoList() {
const { register } = useForm();
return (
<div>
<form>
<input {...register("todo")} placeholder="Write a to do" />
<button>Add</button>
</form>
</div>
);
}
export default ToDoList;
위와 코드량을 비교해보면 터무니 없이 줄어든걸 확인할 수 있다.
handleSubmit: ((data: Object, e?: Event) => void, (errors: Object, e?: Event) => void) => Function
Submit과 validation을 담당하는 기능이다.
두가지 함수가 사용되는데
onValid: 데이터가 유효할때 호출되는 함수다. (필수)
onInvalid: 데이터가 유효하지않을때 호출되는 함수다.
예를 들어 required 구문을 사용할때
<input {...register("email")} required placeholder="Email" />코드를 작성해도 Html에 의해서 Email의 보호를 받지만 element에서 required 를 지우고 등록하는 꼼수를 사용할수도 있다.
<input {...register("email", { required: true })} placeholder="Email" />
하지만 javascript로 안쪽에 valid를 사용하면 그런 꼼수도 차단된다.
또한 아래와 같이 최소길이나 오류메시지도 표현가능하다.
<input
{...register("password", {
required: true,
minLength: {
value: 5,
message: "님 비번 짧음",
},
})}
placeholder="Password"
/>
이 함수는 form 유효성 검사가 성공하면 form 데이터를 받는다.
const { register, handleSubmit } = useForm();
const onSubmit: SubmitHandler = data => console.log(data);
form onSubmit={handleSubmit(onSubmit)}
error가 난 form과 이유에 대해서 출력해주는 함수다.
import { useState } from "react";
import React from "react";
import { useForm } from "react-hook-form";
// function ToDoList() {
// const [toDo, setTodo] = useState("");
// const onChange = (event: React.FormEvent<HTMLInputElement>) => {
// const {
// currentTarget: { value },
// } = event;
// setTodo(value);
// };
// const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
// event.preventDefault();
// console.log(toDo);
// };
// return (
// <div>
// <form onSubmit={onSubmit}>
// <input onChange={onChange} value={toDo} placeholder="Write a to do" />
// <button>add</button>
// </form>
// </div>
// );
// }
interface IForm {
email: string;
firstName: string;
lastName: string;
username: string;
password: string;
password_confirm: string;
}
function ToDoList() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<IForm>({
defaultValues: {
email: "@naver.com",
},
});
const onValid = (data: any) => {
console.log(data);
};
console.log(errors);
return (
<div>
<form
style={{ display: "flex", flexDirection: "column" }}
onSubmit={handleSubmit(onValid)}
>
<input
{...register("email", {
required: "Email is Required",
pattern: {
value: /^[A-Za-z0-9._%+-]+@naver.com$/,
message: "Only naver.com emails allowed",
},
})}
placeholder="Email"
/>
<span>{errors?.email?.message}</span>
<input
{...register("firstName", { required: true })}
placeholder="First Name"
/>
<span>{errors?.firstName?.message}</span>
<input
{...register("lastName", { required: true })}
placeholder="Last Name"
/>
<span>{errors?.lastName?.message}</span>
<input
{...register("username", { required: true, minLength: 10 })}
placeholder="Username"
/>
<span>{errors?.username?.message}</span>
<input
{...register("password", {
required: true,
minLength: {
value: 5,
message: "님 비번 쥰내 짧음",
},
})}
placeholder="Password"
/>
<span>{errors?.password?.message}</span>
<input
{...register("password_confirm", {
required: "Password is required",
minLength: {
value: 5,
message: "what?",
},
})}
placeholder="Password confirm"
/>
<span>{errors?.password_confirm?.message}</span>
<button>Add</button>
</form>
</div>
);
}
export default ToDoList;
IForm인터페이스를 만들어 타입스크립트의 보호를 받도록 그안에 각 데이터 타입을 정리해두고
const {
register,
handleSubmit,
formState: { errors },
} = useForm<IForm>({
defaultValues: {
email: "@naver.com",
},
});
다음과 같이 입력해주자
<span>{errors?.username?(register에 사용된 변수이름).message}</span>
출력부분은 이렇게 사용하면된다.
오류 메시지를 띄우는 방법은 2가지가 있는데 첫번째는
<input
{...register("email", {
required: "Email is Required",
pattern: {
value: /^[A-Za-z0-9._%+-]+@naver.com$/,
message: "Only naver.com emails allowed",
},
})}
placeholder="Email"
/>
required 부분에 입력하는 법과
<input
{...register("password", {
required: true,
minLength: {
value: 5,
message: "님 비번 짧음",
},
})}
placeholder="Password"
/>
개체에 담아서 출력하는 방법이다.
react-hook-form에서 문자열을 리턴하면, 그건 즉 에러메시지를 리턴한다는 뜻이다.
setError는 문제에 따라 추가적으로 에러를 설정할 수 있게 도와준다.
알아두면 굉장히 유용하다.
또한 form에서 내가 고른 input 항목에 강제로 focus가 가능하다.
const onValid = (data: IForm) => {
if (data.password !== data.password_confirm) {
setError("password_confirm", { message: "Password are not the same" });
}
setError("extraError", { message: "Server offline" });
};
위 코드는 패스워드와 패스워드 확인이 틀렸을때
그리고 폼전체의 오류 메시지를 설정한것이다.
<span>{errors?.username?.message}</span>
여기서 신경써야 될건 ?를 붙여야한다는것이다. ?를 붙이면 그항목이 undefined이면 그 뒤를 실행하지 않는다.
하나 더 해보자
예를 들어 nico라는 이름을 가진 사람들은 가입하지 못하는 validate를 만들어 보자
<input
{...register("firstName", {
required: true,
validate: (value) =>
value.includes("nico") ? "no nicos allowed" : true,
})}
placeholder="First Name"
/>
input에 여러개의 검사가 필요할때에는 여러함수를 넣을 수도있다.
<input
{...register("firstName", {
required: true,
validate: {
noNico: (value) =>
value.includes("nico") ? "no nicos allowed" : true,
noNick: (value) =>
value.includes("nick") ? "no nicks allowed" : true,
}
})}
placeholder="First Name"
/>
위와 같이 말이다.
정리
js<span>{errors?.email?.message}</span>
)js = useForm<IForm>({ defaultValues: { email: "@naver.com", // 여기에 @naver.com이라고 설정하면 기본 값으로 @naver.com이 박혀서 나옴 }, });
)