티스토리 뷰
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 수업자료
'정리용 > react' 카테고리의 다른 글
[React] use-hook-form : Props Drilling 과 동적 폼 (0) | 2025.02.28 |
---|---|
[React] useForm 사용 - 종료 (0) | 2025.02.28 |
[React] useForm 사용 - 시작 단계 (0) | 2025.02.28 |
[React] React Hook Form 정리 (0) | 2025.02.28 |
[React - 상태 관리] Redux - Middleware (0) | 2025.02.26 |
- Total
- Today
- Yesterday
- acas#acas7기
- memo
- ssh
- useReducer
- asac7
- Nginx
- useMemo
- useRef
- asac#asac7기
- git
- useCallback
- acac
- react
- useContext
- useLayoutEffect
- asac7기
- useEffect
- ASAC
- asac7#asac
- useState
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |