티스토리 뷰

1. formState 

:: 렌더링 성능을 위해 프록시 패턴을 적용 => 리렌더링 X

:: 리렌더링 X이므로 formState를 보기 위해서는 이벤트 발생이 필요

  • 최초 useForm을 생성 후, 모든 인풋들을 register or control로 일괄 등록 후,
    아래와 같이 formState로 상태 조회
const {
  formState: {
  	// A. 유효성 관련
	errors,           // 모든 인풋들의 각각의 유효성 출력
	isValid,          // 모든 인풋들이 유효한지 여부
	isValidating,     // 모든 인풋들이 유효성 검사중인지
	validatingFields, // 모든 인풋들중 유효성 검사중인것들만 출력
		
	// B. 기본값 대비 입력되었는지 여부
	defaultValues,    // 모든 인풋들의 등록된 defaultValues 출력
	isLoading,        // 모든 인풋들에 비동기로 defaultValues 입력 시 (fetch) 진행중 여부
	isDirty,          // 모든 인풋들이 (defaultValues 등록되어있단 전제하에) 값이 바뀌었는지 확인
	dirtyFields,      // 모든 인풋들중 (defaultValues 등록되어있단 전제하에) 값이 바뀐것들만 출력
	touchedFields,    // 모든 인풋들중 입력이 발생한것들만 출력

	// C. 제출 관련
	isSubmitted,        // 제출되었는지
	isSubmitting,       // 제출중인지
	isSubmitSuccessful, // 제출되었는지
	submitCount,        // 얼마나 제출했는지

	// D. 기타
	disabled,         // useForm 에 모든 인풋에 대해 disabled: true 옵션이 켜져있는지
  }, 
} = useForm();

1.1 formState.error

:: 각 인풋별로 설정된 name을 키값으로 하여 유효성에 위해된 [ 타입 + 메세지 + 출처] 확인 가능

  • 구성
    1) type :: validation 유효성 종류에 게시된 종류
    2) message :: 유효성 규칙을 등록할 때, 경고 메세지 등록 시 표기
    3) ref :: 유효성이 위배된 인풋의 출처

1.2 Ex

더보기

1. 이메일 형식 검증

<input
  name="email"
  {...register("email", {
    required: { value: true, message: "이메일을 입력해주세요" },
    pattern: {
      value: /^\S+@\S+$/i,
      message: "이메일 형식이 올바르지 않습니다",
    },
  })}
/>
<div>
  {errors?.email?.message}
</div>

 

2. 아이디 문자열 최대 길이 제한

<input
  name="username"
  {...register("username", {
    required: { value: true, message: "아이디를 입력해주세요" },
    maxLength: {
      value: 10,
      message: "아이디는 10자 이상이 될 수 없습니다",
    },
  })}
/>
<div>
  {errors?.username?.message}
</div>

 

3. 나이 제한

<input
  name="age"
  {...register("age", {
    required: { value: true, message: "나이를 입력해주세요" },
    min: 10,
  })}
/>
<div>
  {errors?.age?.type === 'min' && '나이는 10살을 넘을 수 없습니다'}
  {errors?.age?.message}
</div>

 

4. 비밀번호 확인

<input
  name="passwordConfirm"
  type="password"
	{...register("passwordConfirm", {
	  required: {
	    value: true,
	    message: "비밀번호 확인을 입력해주세요",
	  },
	  validate: {
      equals: (value, { password }) => value === password || "비밀번호가 일치하지 않습니다."
    },
	})}
/>
<div>
	{errors?.passwordConfirm?.message}
</div>

 


1-1 useFormState()

:: 입력 컴포넌트와 오류 메세지 컴포넌트 분리

:: 프록시를 통해 특정 상태 값만 선택적 구독 -> 불필요한 리렌더링 방지

더보기
function RegistrationForm() {
  const methods = useForm()
  const { register, watch, reset } = methods

  return (
    <FormProvider {...methods}>
      <h3 style={{ margin: 0 }}>유저 추가하기</h3>
      <form className='form-wrapper' onSubmit={methods.handleSubmit((data) => console.log(JSON.stringify(data)))}>
        <UsernameInput />
        <UsernameErrorMessage />
      </form>
    </FormProvider>
  )
}

 - 상위의 form구성


function UsernameInput() {
  const {
    register,
    formState: { errors }
  } = useFormContext();

  return (
    <div>
      <label className='input-label' htmlFor='username'>유저이름 : </label>
      <span className='input-area'>
        <input
          id='username'
          className={clsx('input', errors?.username && 'error-border')}
          {...register('username', {
            required: true,
            maxLength: {
              value: 10,
              message: '아이디는 10자 이상이 될 수 없습니다',
            },
          })}
        />
      </span>
    </div>
  )
}

- input 파트와 에러 메세지 코드를 분리


function UsernameErrorMessage() {
  const { control } = useFormContext();
  const { errors } = useFormState({ control })

  return (
    <div className={errors?.username ? 'error-text' : ''}>
      {errors?.username?.type === 'required' && '아이디는 필수 입력항목 입니다'}
      {errors?.username?.message}
    </div>
  )
}

- 에러 메세지 코드를 분리

- useFormContext의 control을 통해 전달 받은 값 중
- useFormstate를 통해 특정 상태를 개별 구독 가능


1-1.1 <ErrorMessage>

// 설치
npm install @hookform/error-message

:: react-hook-form에서 사용할 수 있는 오류 메세지 컴포넌트

:: 유효성 등록 시, 메시지 값이 없다면  나타나지 않음

// 사용 예시
function UsernameErrorMessage() {
  const { control } = useFormContext();
  const { errors } = useFormState({ control })

  return (
    <ErrorMessage
      errors={errors}
      name="username"
      render={({ message }) => (
        <div className='error-text'>
		      {message}
		</div>
      )}
    />
  )
}

2. watch()

:: 등록된 단일 입력에 대한 값 조회

:: 등록된 입력 필드의 값을 실시간으로 조회하는 데 사용

:: 입력에 대한 동적 처리 가능 

  • 전체 조회 = watch() → value
  • 부분 조회 = watch(['a', 'b']) → [value, value]
  • 단일 조회 = watch('a') → { key: value, key: value }

:: watch() 사용 시, 최상위 부모인 useForm에 대한 리렌더링 발생

2.1 Ex

더보기

1. 뷰로 사용 - but, 리렌더링 문제 발생 가능

function RegistrationForm() {
  const methods = useForm()
  const { register, watch, reset } = methods

  console.log('- useForm 리렌더링')
  return (
    <FormProvider {...methods}>
      <h3 style={{ margin: 0 }}>유저 추가하기</h3>
      <form className='form-wrapper' onSubmit={methods.handleSubmit((data) => console.log(JSON.stringify(data)))}>
        <UsernameInput />
        <PasswordGroupInput />
        <SubmitButton />
        <ErrorLogButton />
        <PasswordWatch />
      </form>
    </FormProvider>
  )
}

 

- 위의 코드에 대해 password를 추척 - 실시간 추적 but 렌더링 발생

function PasswordWatch() {
  const { watch } = useFormContext();

  return (
    <>{watch('password')}</>
  )
}

 


 

2. 롤백 사용

:: 뷰로 보여주는 것이 아니면 콜백 방식 사용

:: 리렌더링 방지 가능

  useEffect(() => {
	  console.log('watch initialized')
    const { unsubscribe } = watch((value) => {
      console.log(value)
    })
    return () => {
	    console.log('page ended, watch destroyed')
			unsubscribe()
    }
  }, [watch])

2-1. useWatch()

:: watch()의 리렌더링을 방지하기 위해 사용

:: 최상위 부모인 useForm에 대한 리렌더링 발생X -> 사용하는 단위 컴포넌트만 리렌더

  • 전체 조회 = useWatch({ name: 'a' }) → value
  • 부분 조회 = useWatch({ name: ['a', 'b']}) → [value, value]
  • 단일 조회 = useWatch() → { key: value, key: value }
function PasswordWatch() {
  const { control } = useFormContext();
	const watched = useWatch({ name: 'password', control });
  return (
    <>{watched}</>
  )
}

3. trigger()

:: 등록된 단일 입력에 대한 유효성 검사 - 유효성 검사를 수동으로 실행

:: 폼을 제출하기 전에 또는 다른 이벤트에서 유효성 검사를 실행 가능

function SubmitButton() {
  const { trigger } = useFormContext();

  return (
    <span>
      <button type='submit'>저장하기</button>
      <button type='button' onClick={() => trigger('passwordConfirm')}>비밀번호 확인</button>
    </span>
  )
}

 


참고

ASAC 수업자료

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함