React

[React/리액트] usestate 남발 코드를 react-hook-form로 리팩토링 하기

solfa 2024. 6. 25. 02:33

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을 사용하여 불필요한 리렌더링을 줄일 수 있다!

 

 

제어 컴포넌트와 비제어 컴포넌트의 차이

 

 

 

https://react-hook-form.com/

 

 

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