폼 라이브러리를 사용하면 굉장히 간편하게 코딩을 할 수 있다. 폼 라이브러리는 검증을 대신해주는 폼, state를 대신해주는 폼 등 종류가 매우 다양하다.
- 제어컴포넌트 : setState발생 ⇢ 리렌더링, 버벅거림, 성능 느림
- 비제어컴포넌트 : 리렌더링 발생하지 않음, 버벅거리지 않음, 성능 빠름
ex) document.getElementById(), useRef
보통 간단한 폼을 사용할 때는 비제어컴포넌트를 사용하고,
신용카드 정보 등 중요한 데이터를 저장하고 있다면 제어 컴포넌트를 이용하는 것이 좋다.
설치 : yarn add react-hook-form
import { useForm } from "react-hook-form";
interface IFormData {
writer: string;
title: string;
contents: string;
boardAddress: {
addressDetail: string;
};
}
export default function ReactHookFormPage() {
const { register, handleSubmit } = useForm<IFormData>();
const onClickSubmit = (data: IFormData) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onClickSubmit)}>
작성자 : <input type="text" {...register("writer")} />
제목 : <input type="text" {...register("title")} />
내용 : <input type="text" {...register("contents")} />
{/* 주소 : <input type="text" {...register("boardAddress.addressDetail")} /> 객체 안에 객체 타입일 때 */}
<button>등록하기</button>
</form>
);
}
{...register("writer")}를 통해서 우리가 이제까지 했던 onChangeWriter, setWriter 등을 대체할 수 있다.
💡register, handleSubmit, form
register : state를 등록하는데 필요한 모든 기능이 들어있습니다.
handleSubmit : resister에 적힌 state를 등록해주는 함수 입니다.
form : 실제 html에 있는 input들을 묶어주는 태그입니다.
💡button type 정리
<button type="button">기본버튼(장바구니담기)</button> <button type="reset">지우기</button> <button type="submit">등록하기</button>
- reset : form 내부의 input 값이 모두 삭제 됩니다.
- submit : form 내부의 input 값이 백엔드로 보내집니다. → 기본값 입니다.
- button : 나만의 버튼을 만들고 싶을때 사용합니다.
- button의 기본 타입은 submit이므로, form 안에도 submit 함수를 만들고, button 자체에도 함수를 만들었을 경우 둘다 실행되게 된다.
yup: 비밀번호가 숫자인지, 문자인지, 최소 8자리인지 등 까다로운 검증 단계를 대신해주는 라이브러리
yup과 같은 검증 라이브러리는 보통 form과 함께 사용하며, 리액트 훅 품과 사용할 수 있도록 잘 정리되어있다.
설치 :
yarn add yup
yarn add @hookform/resolvers
import { useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
const schema = yup.object({
writer: yup.string().required("작성자를 입력해주세요."),
title: yup.string().required("제목을 입력해주세요"),
contents: yup.string().required("내용을 입력해주세요."),
// .min(8, "8개보다 커야됩니다.")
// .max(10, "10개보다 작아야됩니다.")
// .email("이메일 필수입니다.")
});
interface IFormData {
writer: string;
title: string;
contents: string;
// boardAddress: {
// addressDetail: string;
// };
}
export default function ReactHookFormPage() {
const { register, handleSubmit, formState } = useForm<IFormData>({
resolver: yupResolver(schema),
// react hook form에 yup을 등록한 것.
mode: "onChange", // 이렇게하면 리렌더링이 됨.
});
const onClickSubmit = (data: IFormData) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onClickSubmit)}>
작성자 : <input type="text" {...register("writer")} />
<div style={{ color: "red" }}>{formState.errors.writer?.message}</div>
제목 : <input type="text" {...register("title")} />
<div style={{ color: "red" }}>{formState.errors.title?.message}</div>
내용 : <input type="text" {...register("contents")} />
<div style={{ color: "red" }}>{formState.errors.contents?.message}</div>
{/* 주소 : <input type="text" {...register("boardAddress.addressDetail")} /> 객체 안에 객체 타입일 때 */}
<button
style={{ backgroundColor: formState.isValid ? "yellow" : "black" }}
>
등록하기
</button>
</form>
);
}
import * as yup from "yup";
export const schema = yup.object({
writer: yup.string().required("작성자를 입력해주세요."),
title: yup.string().required("제목을 입력해주세요"),
contents: yup.string().required("내용을 입력해주세요."),
// .min(8, "8개보다 커야됩니다.")
// .max(10, "10개보다 작아야됩니다.")
// .email("이메일 필수입니다.")
email: yup
.string()
.email("이메일 형식에 적합하지 않습니다.")
.required("이메일은 필수 입력입니다."),
password: yup
.string()
.min(4, "비밀번호는 최소 4자리 이상 입력해주세요.")
.max(15, "비밀번호는 최대 15자리로 입력해주세요.")
.required("비밀번호는 필수 입력입니다."),
phone: yup
.string()
.matches(/^\d{3}-\d{3,4}-\d{4}$/, "휴대폰 형식에 알맞지 않습니다.")
.required("휴대폰은 필수 입력입니다."),
});
useForm을 커스텀 훅이라고 부른다.