Skip to content

Commit

Permalink
refactor: 로그인/회원가입 폼에 react-hook-form 적용 (#223)
Browse files Browse the repository at this point in the history
* rebase: from develop

* refactor: SignupForm에 rhf 적용완료

* refactor: pr #217에 맞게 수정

* refactor: import 경로 @ 적용

* refactor: 확장자명 tsx -> ts

* fix: button들에 type=button 설정

* fix: button들에 type button 추가 설정

* fix: 닉네임 검사시 누락된 isBlank 함수 추가

* fix: 약관동의를 첫 페이지로

* remove: 중복되는 파일 제거
  • Loading branch information
fecapark authored Jun 23, 2024
1 parent 2bc501c commit 744a2cd
Show file tree
Hide file tree
Showing 15 changed files with 133 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ export const AgreeTerms = ({ onConfirm }: AgreeTermsProps) => {
})}
</StyledDetailTermList>
<BoxButton
type="button"
size="large"
rounding={8}
variant="filled"
Expand Down
4 changes: 3 additions & 1 deletion src/home/components/SignupContents/EmailAuth/EmailAuth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const EmailAuth = ({ email, onConfirm }: EmailAuthProps) => {
{error && <StyledErrorText>{error}</StyledErrorText>}
</div>
<BoxButton
type="button"
rounding={8}
size="large"
variant="filled"
Expand All @@ -51,6 +52,7 @@ export const EmailAuth = ({ email, onConfirm }: EmailAuthProps) => {
</BoxButton>
<StyledPlainButtonContainer>
<PlainButton
type="button"
size="medium"
isPointed={false}
isWarned={false}
Expand All @@ -61,7 +63,7 @@ export const EmailAuth = ({ email, onConfirm }: EmailAuthProps) => {
</PlainButton>
<span>|</span>
<Link to="https://outlook.office.com/mail/" target="_blank" rel="noopener noreferrer">
<PlainButton size="medium" isPointed={false} isWarned={false}>
<PlainButton type="button" size="medium" isPointed={false} isWarned={false}>
학교 메일 열기
</PlainButton>
</Link>
Expand Down
7 changes: 5 additions & 2 deletions src/home/components/SignupContents/EmailForm/EmailForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,21 @@ export const EmailForm = ({ onConfirm }: EmailFormProps) => {
</div>
<StyledButtonsContainer>
<StyledPlainButtonWrapper>
<PlainButton size="medium" isPointed={false} isWarned={false}>
<PlainButton type="button" size="medium" isPointed={false} isWarned={false}>
<StyledSignupButtonText>학교 메일 찾기</StyledSignupButtonText>
</PlainButton>
</StyledPlainButtonWrapper>
<BoxButton
type="submit"
size="large"
variant="filled"
rounding={8}
disabled={email === '' || disabled}
onClick={() => handleClick(onEmailSubmit)}
>
<StyledSignupButtonText>인증 메일 받기</StyledSignupButtonText>
<StyledSignupButtonText>
{disabled ? '잠시만 기다려주세요...' : '인증 메일 받기'}
</StyledSignupButtonText>
</BoxButton>
</StyledButtonsContainer>
</StyledSignupContentContainer>
Expand Down
5 changes: 3 additions & 2 deletions src/home/components/SignupContents/EmailForm/useEmailForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ import { useState } from 'react';

import { postAuthVerificationEmail } from '@/home/apis/authVerification.ts';
import { EmailFormProps } from '@/home/components/SignupContents/EmailForm/EmailForm.type.ts';
import { useFullEmail } from '@/hooks/useFullEmail';
import { useParseFullEmail } from '@/hooks/useParseFullEmail';

export const useEmailForm = ({ onConfirm }: EmailFormProps) => {
const [email, setEmail] = useState('');
const [emailError, setEmailError] = useState<string | undefined>(undefined);
const fullEmail = useFullEmail(email);
const parseFullEmail = useParseFullEmail();

const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setEmail(e.target.value);
};

const onEmailSubmit = async () => {
const fullEmail = parseFullEmail(email);
const res = await postAuthVerificationEmail({ email: fullEmail, verificationType: 'SIGN_UP' });
if (res.data) {
onConfirm(fullEmail);
Expand Down
2 changes: 1 addition & 1 deletion src/home/components/SignupContents/SignupContents.style.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import styled from 'styled-components';

export const StyledSignupContentContainer = styled.div`
export const StyledSignupContentContainer = styled.form`
width: 100%;
display: flex;
flex-direction: column;
Expand Down
2 changes: 1 addition & 1 deletion src/home/components/SignupContents/SignupEnd/SignupEnd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const SignupEnd = () => {
<StyledSignupContentContainer>
<StyledSignupContentTitle>회원가입이 완료되었습니다</StyledSignupContentTitle>
<StyledLinkWrapper to="/login">
<BoxButton rounding={8} size="large" variant="filled">
<BoxButton type="button" rounding={8} size="large" variant="filled">
<StyledSignupButtonText>로그인 페이지로 이동</StyledSignupButtonText>
</BoxButton>
</StyledLinkWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ export interface SignupFormProps {
onConfirm: () => void;
email: string;
}

export interface SignupFormStates {
nickname: string;
password: string;
}
56 changes: 33 additions & 23 deletions src/home/components/SignupContents/SignupForm/SignupForm.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,71 @@
import { BoxButton, PasswordTextField, SimpleTextField } from '@yourssu/design-system-react';
import { useForm } from 'react-hook-form';

import { SignupFormProps } from '@/home/components/SignupContents/SignupForm/SignUpForm.type.ts';
import { useSignUpForm } from '@/home/components/SignupContents/SignupForm/useSignUpForm.ts';
import { usePreventDuplicateClick } from '@/hooks/usePreventDuplicateClick.ts';
import { useSignupFormValidation } from '@/hooks/useSignupFormValidator.ts';
import {
SignupFormProps,
SignupFormStates,
} from '@/home/components/SignupContents/SignupForm/SignUpForm.type.ts';
import { useSignupFormValidation } from '@/hooks/useSignupFormValidation';

import {
StyledSignupButtonText,
StyledSignupContentContainer,
StyledSignupContentTitle,
} from '../SignupContents.style';

import { useSignupFormConfirm } from './useSignupFormConfirm';

export const SignupForm = ({ email, onConfirm }: SignupFormProps) => {
const { nickname, password, onFormConfirm, setNickname, setPassword } = useSignUpForm({
email,
onConfirm,
const { register, handleSubmit, setValue, watch, formState } = useForm<SignupFormStates>({
defaultValues: {
nickname: '',
password: '',
},
});

const { nicknameValidOnce, passwordValidOnce, isFormValid, isNicknameValid, isPasswordValid } =
useSignupFormValidation(nickname, password);
const onFormConfirm = useSignupFormConfirm({ email, onConfirm });

const { disabled, handleClick } = usePreventDuplicateClick();
const { nicknameValidOnce, passwordValidOnce, isFormValid, isNicknameValid, isPasswordValid } =
useSignupFormValidation({
nickname: watch('nickname'),
password: watch('password'),
});

return (
<StyledSignupContentContainer>
<StyledSignupContentContainer onSubmit={handleSubmit(onFormConfirm)}>
<StyledSignupContentTitle>회원가입</StyledSignupContentTitle>
<SimpleTextField
value={nickname}
{...register('nickname', {
onChange: () => {
if (isNicknameValid) nicknameValidOnce.current = true;
},
})}
fieldLabel="사용할 닉네임을 입력해주세요."
helperLabel="한글, 영어, 숫자를 사용해 2~12자로 입력해주세요"
placeholder="ppushoong"
isNegative={!isNicknameValid && nicknameValidOnce.current}
onChange={(e) => {
if (isNicknameValid) nicknameValidOnce.current = true;
setNickname(e.target.value);
}}
onClickClearButton={() => {
nicknameValidOnce.current = false;
setNickname('');
setValue('nickname', '');
}}
/>
<PasswordTextField
{...register('password', {
onChange: () => {
if (isPasswordValid) passwordValidOnce.current = true;
},
})}
fieldLabel="사용할 비밀번호를 입력해주세요."
helperLabel="숫자, 영문자, 특수문자 조합으로 8자 이상 입력해주세요"
placeholder="비밀번호"
isNegative={!isPasswordValid && passwordValidOnce.current}
onChange={(e) => {
if (isPasswordValid) passwordValidOnce.current = true;
setPassword(e.target.value);
}}
/>
<BoxButton
type="submit"
rounding={8}
size="large"
variant="filled"
onClick={() => handleClick(onFormConfirm)}
disabled={!isFormValid || disabled}
disabled={!isFormValid || formState.isSubmitting}
>
<StyledSignupButtonText>회원가입</StyledSignupButtonText>
</BoxButton>
Expand Down
43 changes: 0 additions & 43 deletions src/home/components/SignupContents/SignupForm/useSignUpForm.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { AxiosError } from 'axios';
import { SubmitHandler } from 'react-hook-form';

import { STORAGE_KEYS } from '@/constants/storage.constant';
import { postAuthSignUp } from '@/home/apis/postAuthSignUp';
import { AuthErrorData } from '@/home/types/Auth.type';

import { SignupFormProps, SignupFormStates } from './SignUpForm.type';

export const useSignupFormConfirm = ({ email, onConfirm }: SignupFormProps) => {
const onSignupError = (error: AxiosError) => {
alert(
(error as AxiosError<AuthErrorData>).response?.data.message || '회원가입에 실패했습니다.'
);
};

const onSignupSuccess = () => {
sessionStorage.removeItem(STORAGE_KEYS.EMAIL_AUTH_SESSION_TOKEN);
sessionStorage.removeItem(STORAGE_KEYS.EMAIL_AUTH_SESSION_TOKEN_EXPIRED_IN);
onConfirm();
};

const onFormConfirm: SubmitHandler<SignupFormStates> = async ({ nickname, password }) => {
const sessionToken = sessionStorage.getItem(STORAGE_KEYS.EMAIL_AUTH_SESSION_TOKEN);

if (!sessionToken) {
alert('세션 토큰이 없습니다.');
return;
}

const signUpParams = {
nickName: nickname,
password: password,
sessionToken: sessionToken,
email: email,
};

const { data, error } = await postAuthSignUp(signUpParams);

if (data) onSignupSuccess();
else if (error) onSignupError(error);
};

return onFormConfirm;
};
2 changes: 1 addition & 1 deletion src/home/pages/Login/Login.style.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import styled from 'styled-components';

export const StyledLoginContainer = styled.div`
export const StyledLoginContainer = styled.form`
width: 100%;
display: flex;
Expand Down
Loading

0 comments on commit 744a2cd

Please sign in to comment.