Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- component
- RTK
- go
- backend
- storybook
- JPA
- JavaSpring
- 웹애플리케이션서버
- tanstackquery
- frontend
- javascript
- designpatterns
- react-hook-form
- Gin
- springboot
- satisfiles
- hook
- css
- test
- java
- React
- typescript
- ReactHooks
- golang
- Chakra
- Redux
- 티스토리챌린지
- Spring
- 오블완
Archives
- Today
- Total
bkdragon's log
제출 폼 본문
제출 폼은 UI 요소라고 라기 보단 개발용어(?)의 느낌이다. 물론 아주 개인적인 의견이다.
재사용성 있게 만들어보고 싶어서 만들어보았다! 검증을 편하게 하기 위해 react-hook-form을 사용해서 만들었다. react-hook-form의 간단한 예제 코드로 역할도 할 수 있는 글이 되지 않을까싶다.
우선 전체를 담당하는 Form
form.tsx
interface Props<TFormData extends object> {
title: string;
onSubmit: (data: TFormData) => void;
list: InputList<TFormData>;
}
const Form = <TFormData extends object>({
title,
onSubmit,
list,
}: Props<TFormData>) => {
const { control, handleSubmit } = useForm<TFormData>({ criteriaMode: 'all' });
return (
<div>
<form onSubmit={handleSubmit(onSubmit)} className={styles.form}>
<h2>{title}</h2>
{list.map(({ name, rules, type }, index) => (
<CustomInput
key={index}
type={type}
control={control}
name={name}
rules={rules}
/>
))}
<button className={styles.form_button}>제출</button>
</form>
</div>
);
};
export default Form;
- list를 Props로 받아서 map을 통해 CustomInput 컴포넌트를 화면에 그린다. list에는 name, rules, type이 있다.
- handleSubmit은 TFormData 타입의 데이터를 인자로 받는 submit 함수를 인자로 받아서 사용하면 된다.
- 어떤 title, 어떤 list를 보내냐에 따라 로그인 폼, 결제 폼, 회원가입 폼이 될 수 있다.
fixture.ts
예시로 만든 고정물인데 로그인 폼에 대한 예시이다. 따라서 email과 password가 있다.
export interface LoginFormProps {
email: string;
password: string;
}
type Rules = Omit<
RegisterOptions<FieldValues, string>,
'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'
>;
export type InputList<T extends object> = {
name: Path<T>;
type: React.HTMLInputTypeAttribute;
rules?: Rules;
}[];
export const list: InputList<LoginFormProps> = [
{
name: 'email',
type: 'text',
rules: {
pattern: {
message: '형식에 맞게 입력해주세요.',
value: new RegExp('[a-z0-9]+@[a-z]+.[a-z]{2,3}'),
},
required: '필수 항목 입니다.',
},
},
{
name: 'password',
type: 'password',
rules: {
required: '필수 항목 입니다.',
minLength: { value: 8, message: '8자리 이상이여야 합니다.' },
},
},
];
- name에는 제네릭으로 받은 타입의 key 값이 들어간다.실제 사용할 때 LoginFormProps를 넘겨줄 것이기 때문에 email과 password가 name이 된다.
- type은 input 태크의 type 어트리뷰트에 들어갈 수 있는 값들이 들어간다. (ex. text, password)
- Rules는 react-hook-form에서 가져온 타입이다. 입력 값을 검증하기 위한 옵션들이다. 최대, 최소 길이, 정규식을 이용한 패턴 등을 설정할 수 있다.
이제 이 list를 Form 컴포넌트에 넣어주면 저 rules에 맞게 검증이 이루어지는 CustomInput이 화면에 그려질 것이다. 이제 CustomInput을 보자.
CustomInput.tsx
import { ErrorMessage } from '@hookform/error-message';
import { useController } from 'react-hook-form';
const CustomInput = <TFormData extends object>({
type,
control,
name,
rules,
}: CustomInputProps<TFormData>) => {
const {
field: { onChange, value },
formState: { errors },
} = useController({
name,
control,
rules,
});
return (
<div>
<input
value={value}
onChange={onChange}
type={type}
placeholder={name}
className={styles.form_input}
/>
<ErrorMessage
errors={errors}
name={name}
render={({ messages }) =>
messages &&
Object.entries(messages).map(([type, message]) => (
<p key={type} style={{ color: 'red' }}>
{message}
</p>
))
}
/>
</div>
);
};
export default CustomInput;
- 제어 컴포넌트로 사용하기 위해 useController 훅을 사용한다. 제어 컴포넌트는 컴포넌트(태그)에서 상태를 직접 다루는 것을 말한다.
- 검증 결과 실패시 에러메시지를 보여주는 ErrorMessage 컴포넌트가 있다. react-hook-form 에서 제공된다.
- ErrorMessage 사용법은 정해져있다. 사용하지 않더라도 errors 객체의 값을 map을 통해 뿌려주는 식으로 구현할 수 있다. 공식문서 링크를 남겨두겠다.
- https://www.react-hook-form.com/api/useformstate/errormessage/
다 만들었으니 이렇게 사용하면 된다.
<Form<LoginFormProps> title="Login" onSubmit={onSubmit} list={list} />
결과
UX 측면에서 이점이 있게 사용하려면 할 수 있을 것 같다. Modal 창을 이용한다던지 안내 문구를 추가해서 가이드를 제시한다던지 할 수 있을 것 같다.
이렇게 되면 다음 UI/UX 시리즈는 Modal 이려나?