1편(개념편)에서 이어지는 내용입니다.😎
위 사진처럼 회원가입, 로그인 등 폼을 활용해 사용자에게 입력 필드를 제공할 때 입력과 동시에 유효성 검사를 진행하면 사용자 친화적으로 접근할 수 있어 많은 사이트들이 해당 유효성 검사 방식을 사용합니다.
react-hook-form을 사용해 입력과 동시에 유효성 검증을 구현해보겠습니다.
1. react-hook-form의 기본 설정값
1편에서 설명했듯이 react-hook-form은 submit 함수가 호출 될 때, 즉 사용자가 어떤 행위를 해야 유효성 검증을 진행합니다. 이 부분을 해결하기 위해 입력 필드에 validate 옵션을 통해 함수로 유효성 검사하는 부분을 분리하겠습니다.
2. 구현
[a.기능 설명]
일단 입력 필드의 상태를 세 가지의 타입으로 나눴습니다.
- default : 기본 상태
- error : 유효성 검사 실패 상태
- complete : 유효성 검사 성공 후 입력 완료 상태
[b.유효성 검사 구현]
const Form = () => {
const { register, handleSubmit, formState: { errors } } = useForm();
const validateNickname = (value) => {
if (value.length <= 2) {
return '2글자 이상 입력해주세요.';
}
return true;
};
return (
<form onSubmit={handleSubmit(handleRegistration)}>
<input
type="text"
name="nickname"
ref={register('nickname', {
validate: validateNickname
})}
/>
{errors?.nickname && errors.nickname.message}
<button>Submit</button>
</form>
)
}
react-hook-form에서 제공하는 validate 옵션을 이용해 입력 필드의 값을 받아 유효성을 검증하는 함수를 등록합니다.
errors?.nickname를 통해서 react-hook-form에 등록된 nickname이 errors에 있으면 message를 출력합니다.
[c. 구현]
이제 validate에 등록한 함수에서 반환한 값을 가지고 입력 필드의 스타일을 변경해줍니다.
import styled from "@emotion/styled";
import { useForm } from "react-hook-form";
const Form = () => {
const { register, handleSubmit, formState: { errors } } = useForm();
const validateNickname = (value) => {
if (value.length <= 2) {
return '2글자 이상 입력해주세요.';
}
return true;
};
return (
<form onSubmit={handleSubmit(handleRegistration)}>
<Wrapper error={errors?.nickname && true} >
<Input
type="text"
name="nickname"
ref={register('nickname', {
validate: validateNickname
})}
/>
{errors?.nickname && errors.nickname.message}
</Wrapper>
<button>Submit</button>
</form>
)
}
const Wrapper = styled.div<{error?:boolean}>`
display: flex;
border-bottom: ${props => props.error ? `1px solid red` : `1px solid black` };
color : ${props => props.error ? `red` : `black` };
`;
const Input = styled.input`
height: 40px;
border: none;
outline: none;
`;
이렇게, 사용자가 입력과 동시에 유효성을 확인하는 기능을 react-hook-form을 통해 구현했습니다.
3. 리팩토링
🤔 한 개의 입력 필드에 한 개의 함수가 생성되는 코드가 반복되고 있어, 입력 필드를 컴포넌트로 분리시켜 재사용 할 수 있도록 리팩토링을 진행했습니다.
- Input.tsx
- Form.tsx
Form과 Input으로 컴포넌트를 분리하고 Form에 Input을 등록시키는 방식으로 개발합니다.
[a. 구조 변경 - Form.tsx]
Form과 Input으로 컴포넌트를 분리하고 Form에 Input을 등록시키는 방식으로 개발합니다.
/*Form.tsx*/
import { useForm, useWatch, FormProvider, FieldValues } from "react-hook-form";
interface IFormInputs extends FieldValues {
nickname:string;
}
const Form = () => {
const methods = useForm<IFormInputs>({
defaultValues:{
nickname : ""
},
mode:"onChange"
});
const {
handleSubmit
} = methods;
return(
<FormProvider {...methods} >
<form onSubmit={handleSubmit(onSubmit)}>
<Label>E-mail</Label>
<Input
name="nickname"
rules={{
required:true,
minLength:{
value:2,
message:'2글자 이상 입력해주세요.'
},
}}
type="text"
placeholder="닉네임을 입력해주세요."
/>
</form>
</FormProvider>
)
}
useForm({mode : "onChange"})
- mode 옵션은 onSubmit 이벤트가 발생하기 전 유효성 검사를 언제 할건지를 결정할 수 있습니다. onChange로 설정해 입력 필드가 변경될 때 유효성 검사가 실행되도록 설정합니다.
FormProvider
- react-hook-form에서 제공하는 FormProvider를 사용하면 각 input에 수동으로 props를 전달할 필요 없이 Input이 Form에 등록됩니다.
[b. 구조 변경 - Input.tsx]
/* Input.tsx */
import { useFormContext, useController, FieldPath, FieldValues, UseControllerProps } from "react-hook-form";
interface InputProps<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
>
extends UseControllerProps<TFieldValues, TName>{
type?:string;
placeholder?:string;
}
function Input<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
>
(props:InputProps<TFieldValues, TName>) {
const name = props.name;
const {field, fieldState} = useController({
name,
rules: props.rules
});
return (
<Wrapper error={fieldState.error && true}>
<Input
{...field}
type={props.type}
placeholder={props.placeholder}
/>
{fieldState.error && fieldState.error.message}
</Wrapper>
);
};
export default Input;
- const {field, fieldState} = useController();
- useController hook을 사용해 react-hook-form에 등록되어 있는 입력필드의 상태(fieldState)와 제어되고 있는 입력필드의 현재 값(field)를 가져옵니다.
- field
- onChange, name, ref를 포함하고 있는 개체로 입력 필드에 등록해 줍니다.
- fieldState.error
- 현재 상태를 가져오는 fieldState를 이용해 error 값을 받아오고 error의 값이 있으면 error가 가지고 있는 message 값을 가져올 수 있습니다.
'FRONT > React' 카테고리의 다른 글
[REACT] react-hook-form 사용하기 - 개념(1) (0) | 2022.06.21 |
---|