본문 바로가기

React/공부공부

[react-hook-form] useFrom 활용 (회원가입창)

 

설치하고 import 해준다. 

import { useForm } from "react-hook-form";
import { DevTool } from '@hookform/devtools'

유효성 판별 결과를 편하게 확인할 수 있는 DevTool도 함께 설치해줬다.

 

useForm 의 내장함수들 아래처럼 선언해준다. 사용할 것들만 해줘도 된다. (control은 DevTool용)

const { 
    register
  , control
  , watch
  , handleSubmit
  , reset
  , setValue
  , getValues
  , setError
  , setFocus 
  , clearErrors 
  , trigger
  , formState: { errors, isSubmitting, isDirty, dirtyFields, } 
} = useForm({
    mode: 'onBlur' , 
    defaultValues: {
        userId: ''
      , userPwd: ''
    } ,
  });

 

mode는 유효성 검사 판별 시점이고, defaultValues 에 판별할 변수들을 지정해준다.


* mode 옵션

  - onBlur : input창에서 focus out 되는 시점.

  - onChange : input창 입력값이 변하는 시점.

  - onSubmit : form이 제출되는 시점.

 


 

회원가입_ 입력창 유효성 검사

        <InputItem>
          <label htmlFor='userId'>아이디</label>
          <input
            type="text"
            name="userId"
            placeholder="아이디"
            {...register("userId", {
              required: '아이디는 필수입니다.',
              pattern: {
                value: /^[A-za-z0-9]{5,8}$/ ,
                message: '영문+숫자 최대 5~8자' 
              }
            })
          }
          ></input>
        </InputItem>
        <Err>{errors.userId && <p>{errors.userId.message}</p>}</Err>

 


 

[ register 함수의 옵션들 ]

register 함수의 첫번째 인자를 input의 name으로 지정해서 연결

 

* required (입력값 유무 판별_필수입력값)

위는 required 를 축약형으로 작성한거고,

아래처럼 객체 형태로도 가능 (value값에 변수를 넣어 동적으로 할당 가능)

required: {
    value: true, 
    message: "입력 필수!"
},

* pattern (입력값 정규식 판별)

  pattern: {
    value: /^[A-Za-z]+$/,
    message: "문자만 입력!"
  }

* validate (사용자 정의 유효성 검사)

원하는 판별조건을 함수로 지정할 수 있다. (OR 연산자_ 앞부분이 거짓이라면 뒷부분 출력)

validate: {
	positiveNumber: value => parseFloat(value) > 0 || "양수만 입력!",
	lessThanTen: value => parseInt(value, 10) < 10 || "10이하만 입력!" 
}

이건 아래 비밀번호 확인 창에서 활용했다.

 


 

회원가입_  비밀번호 일치여부 검사

        <InputItem>
          <label htmlFor='userPwd'>비밀번호</label>
          <input
            type="text"
            name="userPwd"
            placeholder="비밀번호"
            {...register("userPwd", {
              required: {
                value: true, 
                message: "비밀번호를 입력해주세요"
              },
              pattern: {
                value: /^[A-za-z0-9]{5,8}$/ ,
                message: '영문+숫자 최대 5~8자' 
              }
              })
            }
          ></input>
        </InputItem>
        <Err>{errors.userPwd && <p>{errors.userPwd.message}</p>}</Err>

        <InputItem>
          <label htmlFor='userPwdChk'>비밀번호 확인</label>
          <input
            type="text"
            name="userPwdChk"
            placeholder="비밀번호 확인"
            {...register("userPwdChk", {
              required: {
                value: true, 
                message: "비밀번호를 한번더 입력해주세요"
              },
              validate: {
                matchPwd: (value) => 
                  value === userPwd || "일치하지 않습니다."
              }
              })
            }
          ></input>
        </InputItem>
        <Err>{errors.userPwdChk && <p>{errors.userPwdChk.message}</p>}</Err>

* watch

form 내부 필드의 변경을 감지하여 변경값을 가져온다. (해당 필드의 값이 변경될 때마다 컴포넌트가 리렌더링

 

* getValues 

호출 시점의 해당 필드의 값을 가져온다. (form의 현재 상태만 읽기 때문에 렌더링X


비밀번호 확인창의 validate 부분은 사용자 지정 함수로 넣어줬다.

  validate: {
    matchPwd: (value) => 
      value === userPwd || "일치하지 않습니다."
  }

const userPwd = watch("userPwd");

비밀번호 필드부분 watch 함수로 잡아왔는데 getValues로 해도 될듯 하다...?


  validate: {
    matchPwd: (value) => 
      value === getValues("userPwd") || "일치하지 않습니다."
  }

 

 


 

회원가입_ 이용약관 동의 (필수 & 선택)

        <TermsItem>
          <label htmlFor='allYn'>
          <input
            type="checkbox"
            name="allYn"
            id="allYn"
            {...register("allYn", {
              required: {
                valueAsBoolean: true
              }
              })
            }
            >
          </input>
          전체동의</label>
        </TermsItem>
        <TermsItem>
        <label htmlFor='termsYn'>
          <input
            type="checkbox"
            name="termsYn"
            id="termsYn"
            {...register("termsYn", {
              required: {
                valueAsBoolean: true
              }
              })
            }
            >
          </input>
          필수동의</label>
        </TermsItem>
        <TermsItem>
        <label htmlFor='optionYn'>
          <input
            type="checkbox"
            name="optionYn"
            id="optionYn"
            {...register("optionYn", {
              required: {
                valueAsBoolean: false
              }
              })
            }
            >
          </input>
          선택동의</label>
        </TermsItem> 
        <Err>{errors.termsYn && <p>{errors.termsYn.message}</p>}</Err>

전체동의, 필수동의, 선택동의 세 개의 체크박스가 있고,

'전체동의' 체크/해제시 '필수동의'&'선택동의' 체크박스도 함께 체크/해제되도록 설정.

 

const allYn = watch("allYn");

이건 변경상태를 감지해서 값을 가져와야 하니 watch를 사용.

useEffect(() => {
  setValue("termsYn", allYn? true : false);
  setValue("optionYn", allYn? true : false);
},[allYn])

'전체동의' 의 체크박스 값이 변경되면  '필수동의'&'선택동의' 체크박스도 함께 변경된다.

 


 

회원가입_ 아이디 중복검사

        <InputItem>
          <label htmlFor='userId'>아이디</label>
          <input
            type="text"
            name="userId"
            placeholder="아이디"
            {...register("userId", {
              required: {
                value: true, 
                message: "아이디 입력은 필수입니다."
              },
              pattern: {
                value: /^[A-za-z0-9]{5,8}$/ ,
                message: '영문+숫자 최대 5~8자' 
              }
              })
            }
          ></input>
        </InputItem>
        <Err>{errors.userId && <p>{errors.userId.message}</p>}</Err>
        <button onClick={(e) => 
          checkId(e, getValues("userId"))}>확인</button>

 

아이디 input창 옆에 button을 만들어주고 클릭이벤트(checkId) 생성. (인자로 이벤트와 아이디값을 가져간다.)

 

// id중복확인 검사.
const checkId = async (e, id) => {

  e.preventDefault();
  const isValidId = await trigger("userId");

  if(!isValidId){
    console.log("유효성검사 실시")
    return;
  }
  
  //서버로 보내 중복여부 확인하는 로직
  console.log("입력한ID : ", id)

}

id 중복확인 버튼을 눌렀을때

e.preventDefault() 로 막아주지 않으면 form 제출 이벤트가 실행되어

전체 필드에 대한 유효성 검사가 실시된다. (모든 에러 메시지가 뜬다.)

 

이후 trigger를 활용해 id필드에 대한 유효성 검사만 실시 **

( useForm의 mode가 onblur이기 때문에 아이디 input창을 활성화 시키지 않으면 유효성 검사가 실행되지 않는다.

input창 활성화 없이 버튼을 눌렀을 때 유효성 검사가 실행되도록 수동으로 설정해준거다. ) 


 

유효성 검사 devtool에서 input창 활성화 여부 및 유효성검사 결과를 확인할 수 있는데

isdirty는 defaultValue와의 값 비교결과여서 다른 정규식에 대해서는 검증이 안된다.

(빈칸일 때)

(정규식에 맞지 않을 때)

이때도 true값이 나온다.

 


* handleSubmit

form을 제출을 위해 useForm에서 제공하는 함수로, 자동으로 유효성 검사 실행 후 form의 데이터를 전달한다.

아래 두 개의 콜백 함수를 인자로 받음.

 - onSubmit : 유효성 검사 성공 시 제출.

 - onError : 유효성 검사 실패 시 실행.

 

  return (
    <>
    <h2>Test</h2>
    <Container>
      <form onSubmit={handleSubmit(onSubmit, onError)}>
      
        <InputItem>
          <label htmlFor='userId'>아이디</label>
          <input
            type="text"
            name="userId"
            placeholder="아이디"
            {...register("userId", {
              required: {
                value: true, 
                message: "아이디 입력은 필수입니다."
              },
              pattern: {
                value: /^[A-za-z0-9]{5,8}$/ ,
                message: '영문+숫자 최대 5~8자' 
              }
              })
            }
          ></input>
        </InputItem>
        <Err>{errors.userId && <p>{errors.userId.message}</p>}</Err>


        <InputItem>
        
        ....
        
        </InputItem>
        ....
        
        
        <button type="submit">제출</button>
      </form>
      <DevTool control={control} />
    </Container>
    </>
  );
}

 

각각의 결과를 출력해보면

성공 시 onSubmit

const onSubmit = (data) => {
  console.log("data: ", data);
}
// 출력결과
// data:  {userId: '입력한ID', ..... , userName: '입력한이름'}

 

실패 시 onError

const onError = (error) => {
  console.log("error: ", error);
}
error:  
	{userId: {…}}
		userId: {type: 'pattern', message: '영문+숫자 최대 5~8자', ref: input}
		[[Prototype]]: Object

 

'React > 공부공부' 카테고리의 다른 글

[ React / ag-grid ] 관련정리  (0) 2024.05.25
[React 참고] eslint-disable / Lint 끄기 (WARNING)  (0) 2024.05.25
[React Hook] useState  (0) 2024.05.18
[React Hook] useRef  (0) 2024.04.14
[React Hook] useEffect  (0) 2024.04.13