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 value={toDo} onChange={onChange} placeholder="Write a to do"/>
<button>Add</button>
</form>
</div>
}
export default ToDoList;
일단 기본적으로 form을 만들어준다.
typescript에서 form은 이렇게 해주면 됐었지~.~
약간 형식?같은거인듯.. 걍 저렇게 하면 됨
저렇게 해주면 우리가 submit하는 값이 콘솔에 찍힌다.ㅇㅇ
그런데 코드가 너무 길고 지루하다,,
만약 input이 여러개 있다면 우리는 onChange도 여러개 만들어줘야한다.
그래서 react-hook-form을 사용하면
이 모든 걸 한 줄의 코드로 작성할 수 있다.
리액트에서 form을 가장 쉽게 관리할 수 있게 해준다.
큰 규모의 form을 다룰 때 매우 유용.
설치
터미널에npm install react-hook-form
입력
우리는 useForm
이라는 hook을 사용할 것이다.
import 해주자!
register
함수가 위의 모든 것을 대체해줄 것이다.
const {register} = useForm();
console.log(register("toDo"));
이렇게 해주고 콘솔에 찍힌 것을 확인해 register함수를 호출했을 때 생기는 일을 알아보자.
짠~~~ 이런 객체를 만들어준다.
onBlur, onChange, ref, name이 생긴다.
<form>
<input {...register("toDo")} placeholder="Write a to do"/>
<button>Add</button>
</form>
이제 이렇게 해주면
register 함수가 반환하는 객체가 input에 props로 전달된다.
useForm을 이용한 한 줄의 코드로 onChange, value, useState가 모두 대체되었다.
여기서 이제 추가로 useForm
을 한 번 더 사용해준다.
이번에는 watch
를 사용해보자.
watch는 form의 입력값들의 변화를 관찰할 수 있게 해주는 함수이다.
const {register, watch} = useForm();
console.log(watch());
해주면
이렇게 됨.
이런 react hook form은 input이 여러개 있을 때 매우매우 효과를 발휘한다고 함!
handleSubmit
으로 onSubmit
을 모두 대체할 수 있다.
handleSubmit
이 validation을 담당하게 된다,,
preventDefault도 담당하고,,, 다 해준다.
form의 onSubmit
이벤트 안에 handleSubmit
을 호출하고, handleSubmit
은 2개의 인자를 받는다.
하나는 데이터가 유효할 때 호출되는 함수, 하나는 유효하지 않을 때 호출되는 함수이다.
후자(onInValid)는 필수가 아니지만, 전자(onValid)는 필수임.
import { useState } from "react";
import {useForm} from 'react-hook-form'
function ToDoList(){
const {register, handleSubmit} = useForm();
const onValid = (data:any) => {
console.log(data);
}
return (
<div>
<form onSubmit = {handleSubmit(onValid)}>
<input {...register("Email")} placeholder="Email"/>
<input {...register("firstName")} placeholder="First Name"/>
<input {...register("lastName")} placeholder="Last Name"/>
<input {...register("username")} placeholder="Username"/>
<input {...register("password")} placeholder="Password"/>
<input {...register("password1")} placeholder="Password1"/>
<button>Add</button>
</form>
</div>
)
}
export default ToDoList;
이렇게 해주고 각 정보를 채워서 Add 버튼을 누르면
wow 이렇게된다. 매우 간단하고 대박임
유저가 submit을 하면 handleSubmit
이 validation과 해야할 일을 모두 마치고 데이터가 유효할 때만 함수를 호출한다. wowwww
이제 form이 유효하지 않을 때를 생각해보자.
register
함수 안에 {required : true}
를 추가해주면 HTML에 의지하는 것이 아닌, 자바스크립트에서 validation을 할 수 있게 된다.
저렇게 해주면 빈 input이 있는데 Add를 눌렀을 때,
자동으로 비어있는 input에 마우스 커서를 focus해준다🤭🤭🤭🤭
(당연히 submit되지 않는다.)
추가로 register
함수에 minLength
만 추가해준다면
텍스트 길이가 그 설정값을 넘지 않을 때를 알아서 validation 해준다.
아 추가로 required
에 true를 설정해주면 그 값이 필수라는 것을 설정해준 것이고,
required
에 메세지를 넣어준다면 유저에게 보여줄 화면에 나올 에러 객체에 들어가게 된다.
minLength
도 마찬가지로 그냥 최소 글자수를 보낼 수도 있고, 객체를 보내서 value값과 메세지를 전달할 수 있다.
진짜 매우 대박인 기능
useForm
의 formState
를 사용해보자.
console에 formState.errors
를 찍어보면...
알아서 에러를 표시해준다. omg
어디서 에러가 났는지, 어떤 에러가 난건지 알려준다.
register
함수에 pattern
을 추가해주자
pattern
에는 정규식을 복붙해주자
<input
{...register("Email", {
required: true,
pattern: {
value:/^[A-Za-z0-9._%+-]+@naver.com$/,
message: "Only naver.com emails allowed",
},
})}
placeholder="Email"
/>
자 이런식으로 해주고 콘솔에서 한 번 확인해보자.
email칸에 @naver.com 형식이 아닌 값을 넣어서 Add를 클릭해보면 콘솔창에는 Only naver.com emails allowed
라는 메세지가 출력된다.
물론 error type은 pattern이 되겠지?
이제 에러를 콘솔에서만 보지 말고 유저화면에 보여줘보자
<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>
이러면 이제 이메일이 @naver.com 형식으로 작성되지 않았을 경우 message를 화면에 출력한다.
다른 input들도 다 같은 방식으로 작성해주면 된다.
interface설정도 추가로 해주면 되겠다.
const {
register,
handleSubmit,
formState: { errors },
} = useForm<IForm>({
defaultValues: {
email: "@naver.com",
},
});
이런식으로 default값도 설정해줄 수 있다.
password와 password1의 값이 다르면 에러를 출력해보자.
비밀번호 확인 기능이 되겠다.
data 형식을 IForm(interface이름)으로 바꿔주고
const onValid = (data:IForm) => {
if (data.password!==data.password1){
setError("password1", {message: "Password are not the same"})
}
}
해주자.
이렇게 했을 때 비밀번호가 일치하지않으면
이런식으로 출력된다.
아그리고 setError가 좋은게
에러가 출력된 input으로 마우스를 자동포커싱해준다!!
만약 비밀번호 불일치 에러가 났을 때 마우스를 password1 input으로 강제포커싱 하고싶다면,
저 위 코드 message 객체 뒤에 shouldFocus: true
해주면 된다.
만약 username에 "초연"이 포함되어있다면, 에러를 발생시켜보자.
이것을 위해 register에 validate를 추가해준다.
이 함수는 인자로 항목에 현재 쓰여지고 있는 값을 받는다.
<input
{...register("username", {
required: "write here",
minLength: 2,
validate: (value)=>!value.includes("초연"),
})}
placeholder="Username"
/>
이렇게 해주자.
이러면 value에 "초연"이 포함되지 않는다면 true를 반환한다.
위 두 사진에서 알 수 있듯이 username(4번째 input)에 "초연"이 포함되면 validate에 에러가 난다. Good
validate: (value)=>
value.includes("초연") ? "no 초연 allowed" : true,
이렇게 해주면 만약 유저가 username에 "초연"을 포함해 작성한 경우 유저에게 "no 초연 allowed"
메세지를 보여주고 "초연"이 포함되지않은 경우엔 그대로 submit된다.
validate:{
noChoyeon:(value)=>
value.includes("초연") ? "no 초연 allowed" : true,
noNick: (value)=>
value.includes("nick") ? "no nick allowed" : true,
},
이러면 이제 "초연"과 "nick" 모두 제한된다.
이렇게 우리가 원하는대로 validation해줄 수 있다.
import React from "react";
import {useForm} from 'react-hook-form'
interface IForm {
toDo: string;
};
function ToDoList(){
const { register, handleSubmit, setValue } = useForm<IForm>();
const handleValid = (data: IForm) => {
console.log("add to do", data.toDo);
setValue("toDo", "");
};
return (
<div>
<form onSubmit={handleSubmit(handleValid)}>
<input
{...register("toDo", {
required: "Please write a To Do",
})}
placeholder="Write a to do"
/>
<button>Add</button>
</form>
</div>
)
}
export default ToDoList;
이제 모든걸 지우고 여기서 다시 투두리스트 만들기를 시작한다.
setValue로 빈 문자열을 설정해주면 조건이 통과되어 submit되었을 때 input안을 비워준다.