일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- React
- designpatterns
- javascript
- 오블완
- ReactHooks
- java
- react-hook-form
- frontend
- JPA
- storybook
- Gin
- 웹애플리케이션서버
- backend
- satisfiles
- test
- springboot
- Spring
- Chakra
- golang
- component
- css
- go
- 티스토리챌린지
- JavaSpring
- hook
- Redux
- RTK
- typescript
- tanstackquery
- Today
- Total
bkdragon's log
[React-hook-form] register, handleSubmit 살짝 파헤치기 본문
React-hook-form은 form을 통해 제출할 내용의 유효성 검사 과정을 간편하게 해주고 불필요한 렌더링도 줄여주는 라이브러리이다. 사용 예시를 살펴보고 주요 역할을 하는 register와 handleSubmit에 대해 살펴보려고 한다.
React-hook-form 기본 사용법 예시
import React from 'react';
import { useForm } from "react-hook-form";
interface FormData {
writer : string;
title : string;
contents : string;
}
const ReactHookFromPage = () => {
const { register, handleSubmit, watch, formState: { errors } } = useForm<FormData>();
const onSubmit = (data : FormData) => {
console.log(data)
}
return (
<div>
<form onSubmit={handleSubmit(onSubmit)}>
작성자 : <input type='text' {...register('writer')} />
제목 : <input type='text' {...register('title')}/>
내용 : <input type='text' {...register('contents')}/>
<button>등록</button>
{/* 버튼의 기본 값은 submit, 클릭시 자동으로 form의 onSubmit이 실행됨.*/}
</form>
</div>
);
};
export default ReactHookFromPage;
React-hook-form 없이는 writer에 대한 state, title state, contents state도 다 만들어야 했을 것이다. 코드의 양이 확 줄어들었다.
register
입력 또는 선택 요소를 등록하고 유효성 검사 규칙을 React-hook-form에 적용할 수 있다. 사용자의 커스텀 유효성 검사도 허용한다.
const { onChange, onBlur, name, ref } = register('firstName');
<input
onChange={onChange} // assign onChange event
onBlur={onBlur} // assign onBlur event
name={name} // assign name prop
ref={ref} // assign ref prop
/>
// 위처럼 쓸 수 있지만 아래와 같이 쓰는 편이 편하다. register가 onChange와 같은 것들을
// 가지는 객체를 리턴하고 이것을 input 태그에 뿌려주는것으로 사용이 완료된다.
<input {...register('firstName')} />
좀 더 들어가서 타입을 확인해보면 다음과 같다. (register의 타입)
타입을 확인할 때 세세하게 살펴보는 것도 좋지만 간단하게 인자와 리턴 타입만 봐도 어느정도의 활용법을 이해하는데에는 문제 없다.
// register의 타입
export type UseFormRegister<TFieldValues extends FieldValues> = <TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>(name: TFieldName, options?: RegisterOptions<TFieldValues, TFieldName>) => UseFormRegisterReturn<TFieldName>;
// register의 리턴 타입
export type UseFormRegisterReturn<TFieldName extends InternalFieldName = InternalFieldName> = {
onChange: ChangeHandler;
onBlur: ChangeHandler;
ref: RefCallBack;
name: TFieldName;
min?: string | number;
max?: string | number;
maxLength?: number;
minLength?: number;
pattern?: string;
required?: boolean;
disabled?: boolean;
};
필드의 이름을 첫번째 인자로 받고 옵션들을 두번째 인자로 받는다. 이때 받은 필드의 이름을 활용해 onChage, onBlur, ref의 것들을 반환한다.
ref를 반환하는 점에서 비제어 컴포넌트의 방식을 사용하는 것을 알 수 있다. 이로 인해 값이 변할 때 리렌더링이 발생하지 않아 성능적인 이점을 챙길 수 있다. onChange가 있는 것은 실시간으로 값을 추척하는 watch를 사용하기 위함이라고 생각할 수 있다.
옵션의 타입도 살펴보자. (복잡한데 쫄지말자 그냥 객체인데 이것저것 옵셔널(Partial)로 들어가있는거다)
xport type RegisterOptions<TFieldValues extends FieldValues = FieldValues, TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = Partial<{
required: Message | ValidationRule<boolean>;
min: ValidationRule<number | string>;
max: ValidationRule<number | string>;
maxLength: ValidationRule<number>;
minLength: ValidationRule<number>;
validate: Validate<FieldPathValue<TFieldValues, TFieldName>, TFieldValues> | Record<string, Validate<FieldPathValue<TFieldValues, TFieldName>, TFieldValues>>;
value: FieldPathValue<TFieldValues, TFieldName>;
setValueAs: (value: any) => any;
shouldUnregister?: boolean;
onChange?: (event: any) => void;
onBlur?: (event: any) => void;
disabled: boolean;
deps: InternalFieldName | InternalFieldName[];
}> & ({
pattern?: ValidationRule<RegExp>;
valueAsNumber?: false;
valueAsDate?: false;
} | {
pattern?: undefined;
valueAsNumber?: false;
valueAsDate?: true;
} | {
pattern?: undefined;
valueAsNumber?: true;
valueAsDate?: false;
});
다음과 같은 값들을 옵션에 추가할 수 있다. 이름만으로 유추가 가능하다. 최소 길이나 최대 길이 같은 것들을 설정할 수 있다.
ValidationRule 타입까지 살펴보면 이해에 더 도움을 줄 것 같다.
export type ValidationValue = boolean | number | string | RegExp;
export type ValidationRule<TValidationValue extends ValidationValue = ValidationValue> = TValidationValue | ValidationValueMessage<TValidationValue>;
export type ValidationValueMessage<TValidationValue extends ValidationValue = ValidationValue> = {
value: TValidationValue;
message: Message;
};
boolean | number | string | RegExp 이런 값들을 받아서 유효성 검사를 하고 메시지를 보여주는구나 정도로 이해하면 되겠다.
비밀번호에 minLength를 적용한다 하면 number를 value로 받고 유효성 검사 실패시 넣을 메시지를 작성해서 주면 되겠다.
<form onSubmit={handleSubmit(onSubmit)}>
작성자 : <input type='text' {...register('writer', {required : {message : 'this is requred', value : true}})} />
제목 : <input type='text' {...register('title')}/>
내용 : <input type='text' {...register('contents')}/>
비밀번호 : <input type='password' {...register('password', {minLength : {value : 8, message : '비밀번호는 최소 8자리여야 합니다.'}})}/>
<button>등록</button>
</form>
handleSubmit
유효성 검사를 성공한 데이터를 얻을 수 있는 함수이다.
마찬가지로 타입을 살펴보면,
export type UseFormHandleSubmit<TFieldValues extends FieldValues, TTransformedValues extends FieldValues | undefined = undefined> = (onValid: TTransformedValues extends FieldValues ? SubmitHandler<TTransformedValues> : SubmitHandler<TFieldValues>, onInvalid?: SubmitErrorHandler<TFieldValues>) => (e?: React.BaseSyntheticEvent) => Promise<void>;
핸들러를 인자로 받는데 이 핸들러는
export type SubmitHandler<TFieldValues extends FieldValues> = (data: TFieldValues, event?: React.BaseSyntheticEvent) => unknown | Promise<unknown>;
register로 등록한 필드들이 들어있는 데이터(객체)와 event를 받는다.
글의 시작부에 첨부한 코드처럼 useFrom에 제너릭으로 준 타입과 같은 객체를 첫번째 인자로 받는 handler 함수를 만들어서 handleSubmit에 인자에 넣고 그것을 form의 onSubmit에 사용해주면 된다.
공식 문서와 내부 타입을 살펴보며 간단하게? 파헤쳐보았다.
'React' 카테고리의 다른 글
[React] Virtual DOM (0) | 2023.06.25 |
---|---|
[React Query] Mutation 이후 캐시 데이터 직접 변경하기 (0) | 2023.06.24 |
커링과 HOC (0) | 2023.05.31 |
[React-Query] 캐싱, 헷갈리는 부분 (0) | 2023.05.26 |
컴포넌트 분리하기 전략 (0) | 2023.05.24 |