To-Do

제동현·2023년 2월 15일
0
post-thumbnail

React - Hook - Form

install

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

register: (name: string, RegisterOptions?) => ({ onChange, onBlur, name, ref })
이 메서드를 사용하면 onChange 이벤트 핸들러도 props도 setState도 필요없다.
input을 등록하거나 element를 선택하고 유효성 검사 규칙을 React Hook Form에 적용할 수 있다.
유효성 검사 규칙은 모두 HTML 표준을 기반으로 하며 사용자 지정 유효성 검사 방법도 허용한다.

watch

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

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)}

formstate

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

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"
        />

위와 같이 말이다.

정리

  1. useForm을 import
  2. 컴포넌트에서 useForm을 호출하면 register와 handleSubmit이 제공
  3. register 함수를 form에 있는 모든 input에서 호출
  4. 검사옵션을 설정 한가지 옵션은 그냥 required: true를 쓰거나 아니면 정규식을 쓰거나 내가 만든 함수로 검사할수도있다.
  5. error객체는 formState 내부에 있는데 formState에는 form의 state가 들어있고, 우리는 그 중 error 객체가 필요하다.
    그래서 errors를 formState의 내부에서 가져온 다음, html 태그 안에 넣어준다.
    (ex : js<span>{errors?.email?.message}</span>)
  6. form에 기본값을 설정해주려면 defaultValues라고 적고 원하는 항목을 적어주면된다
    (ex js = useForm<IForm>({ defaultValues: { email: "@naver.com", // 여기에 @naver.com이라고 설정하면 기본 값으로 @naver.com이 박혀서 나옴 }, });)
  7. setError 함수는 코드에서 에러를 발생시킬때 매우 유용하다. 유저가 잘못된 정보를 입력하게 되면 우리가 그들에게 알려준다.
  8. shouldFoucs라는건, 사용자가 form을 submit할 때 에러를 발생시키면, 커서를 해당 input에 focus시켜주는 기능이다.

0개의 댓글