usestate 남발 코드를 react-hook-form로 리팩토링 하기로 하였다
현재 코드는 비밀번호 재설정 기능을 구현하고 있는 상태
기존 코드
import { EMAIL_DOMAIN } from '@/constants/email.constant';
import { BoxButton, SuffixTextField } from '@yourssu/design-system-react';
import { StyledEmailContainer, StyledSubTitleText, StyledTitleText } from './EmailInput.style';
import { useState } from 'react';
import { postAuthVerificationEmail } from '@/home/apis/authVerification';
import { useFullEmail } from '@/hooks/useFullEmail';
interface EmailInputProps {
email: string;
onConfirm: (email: string) => void;
}
export const EmailInput = ({ email, onConfirm }: EmailInputProps) => {
const [localEmail, setLocalEmail] = useState(email);
const [emailError, setEmailError] = useState(false);
const [emailSending, setEmailSending] = useState(false);
const fullEmail = useFullEmail(localEmail);
const handleChange = (value: string) => {
setLocalEmail(value);
setEmailError(false);
};
const handleEmailSubmit = async () => {
if (!localEmail) {
setEmailError(true);
return;
}
setEmailSending(true);
try {
const response = await postAuthVerificationEmail({
email: fullEmail,
verificationType: 'PASSWORD',
});
if (response.error) {
setEmailError(true);
} else {
onConfirm(localEmail);
}
} catch (error) {
setEmailError(true);
} finally {
setEmailSending(false);
}
};
return (
<>
<StyledTitleText>비밀번호 찾기</StyledTitleText>
<StyledSubTitleText>
숨쉴에 가입했던 학교 이메일을 입력해주세요.
<br />
비밀번호 재설정 메일을 보내드립니다.
</StyledSubTitleText>
<StyledEmailContainer>
<SuffixTextField
fieldLabel="학교 이메일"
placeholder="ppushoong"
suffix={EMAIL_DOMAIN}
value={localEmail}
onChange={(e) => handleChange(e.target.value)}
isNegative={emailError}
helperLabel={emailError ? '존재하지 않는 이메일입니다.' : ''}
/>
</StyledEmailContainer>
<BoxButton
style={{ width: '100%' }}
size="large"
variant="filled"
rounding={8}
onClick={handleEmailSubmit}
disabled={emailSending}
>
재설정 메일 보내기
</BoxButton>
</>
);
};
보다시피 useState가 굉장히 많은 걸 알 수 있다!
이런 걸 제어 컴포넌트라고 하고 이 친구는 실시간으로 동기화되는 특징이 있다.
하지만 이런 식으로 코드를 사용하게 되면 하나하나의 state가 바뀔 때 마다 리렌더링 되는 문제가 발생한다.
그러면 어떻게 코드를 효율적으로 바꾸느냐!
=> 비제어 컴포넌트인 react-hook-form을 사용하여 불필요한 리렌더링을 줄일 수 있다!
제어 컴포넌트와 비제어 컴포넌트의 차이
react-hook-form을 사용한 코드
import { EMAIL_DOMAIN } from '@/constants/email.constant';
import { BoxButton, SuffixTextField } from '@yourssu/design-system-react';
import { StyledEmailContainer, StyledSubTitleText, StyledTitleText } from './EmailInput.style';
import { postAuthVerificationEmail } from '@/home/apis/authVerification';
import { useFullEmail } from '@/hooks/useFullEmail';
import { useForm } from 'react-hook-form';
interface EmailInputProps {
email: string;
onConfirm: (email: string) => void;
}
interface EmailInputProps {
email: string;
onConfirm: (email: string) => void;
}
interface FormData {
email: string;
}
export const EmailInput = ({ email, onConfirm }: EmailInputProps) => {
const {
register,
handleSubmit,
watch,
formState: { errors, isSubmitting },
setError,
} = useForm<FormData>({
defaultValues: { email },
});
const localEmail = watch('email');
const fullEmail = useFullEmail(localEmail);
const handleOnSubmit = async (data: FormData) => {
const response = await postAuthVerificationEmail({
email: fullEmail,
verificationType: 'PASSWORD',
});
if (response.error) {
setError('email', { type: 'manual', message: '존재하지 않는 이메일입니다.' });
} else {
onConfirm(data.email);
}
};
return (
<>
<StyledTitleText>비밀번호 찾기</StyledTitleText>
<StyledSubTitleText>
숨쉴에 가입했던 학교 이메일을 입력해주세요.
<br />
비밀번호 재설정 메일을 보내드립니다.
</StyledSubTitleText>
<StyledEmailContainer>
<SuffixTextField
fieldLabel="학교 이메일"
placeholder="ppushoong"
suffix={EMAIL_DOMAIN}
{...register('email', { required: '이메일을 입력해주세요.' })}
isNegative={!!errors.email}
helperLabel={errors.email ? errors.email.message : ''}
/>
</StyledEmailContainer>
<BoxButton
style={{ width: '100%' }}
size="large"
variant="filled"
rounding={8}
onClick={handleSubmit(handleOnSubmit)}
disabled={isSubmitting}
>
재설정 메일 보내기
</BoxButton>
</>
);
};
이런식으로 바꿀 수 있다! 하나하나 설명을 해보자면
- 컴포넌트 및 훅 임포트:
- useForm 훅은 react-hook-form에서 폼 상태 관리를 위해 사용된다.
- 타입 정의:
- FormData는 폼 데이터의 타입을 정의한다. 이메일 하나만 제출하는 화면이라 이메일만 포함시켰다.
- useForm 설정:
- useForm 훅을 사용하여 폼 상태와 유효성 검사를 설정한다. 초기값으로 defaultValues에 email을 설정한다.
- register, handleSubmit, watch, formState, setError를 구조 분해 할당으로 추출한다.
- 이메일 감지 및 변환:
- watch('email')을 사용하여 사용자가 입력하는 이메일 값을 실시간으로 감지한다.
- onSubmit 함수:
- handleSubmit 함수는 폼 제출 시 onSubmit 함수를 호출한다.
- handleOnSubmit 함수는 비동기 함수로, 이메일 주소를 포함하여 비밀번호 재설정 요청을 보낸다.
- postAuthVerificationEmail API 호출이 성공하면 onConfirm 함수가 호출되고 실패하면 setError를 사용하여 오류 메시지를 설정한다.
- 렌더링:
- BoxButton을 사용하여 폼 제출 버튼을 생성합니다. handleSubmit(onSubmit)으로 버튼 클릭 시 폼이 제출되도록 설정합니다. isSubmitting 상태를 사용하여 버튼을 비활성화한다.
이런식으로 쓰면 된다
isSubmitting상태를 쓸 수 있는게 좋음!
그리고 전체를 감싸주는 태그가 <form> 이어야 한다.
div로 두어도 되지만 rhf의 이점을 최대한 살리기 위해서는 form 태그로 감싸주는게 좋은 것 같다.
예를 들면 form은 엔터키로 다음 넘어갈 수 있는 것도 되고....
암튼 그렇다!
훨씬 깔끔한 코드가 되어 기분이 좋다
근데 폼 제출과 같은 isSubmitting 상태를 굳이 쓸 일이 없다면 rhf를 도입하는 건 굳이?? 과도한 최적화가 될 수도 있다!
state가 하나만 있다거나 input할 것들이 없을 때는 그냥 기존 코드를 유지해도 좋다!
줏대있게 개발하자.
728x90
'React' 카테고리의 다른 글
[React/리액트] ThemeProvider로 전역으로 디자인 시스템 설정하기 (0) | 2024.07.20 |
---|---|
[React/리액트] Controlled와 Uncontrolled 컴포넌트: 리액트에서 폼 입력 관리하기 (0) | 2024.06.29 |
[React] 리액트 무한스크롤 예제 및 동작 과정 설명 (1) | 2024.05.27 |
[React] 리액트 debounce로 검색 성능 최적화하기 (0) | 2024.05.22 |
[React] 리액트 상태 업데이트 할 때 update function 사용하기 (0) | 2024.05.05 |