저번 시간에 Portal을 이용해 모달을 구현하려 했지만 상단 header바에 사용하기 적합하지 않다는
팀원의 의견을 반영해서 독립된 컴포넌트를 만들어 적용하기로 했다.
우선 헤더에 사용할 모달 컴포넌트의 코드를 보자
import { ReactComponent as HomeSVG } from 'assets/svg/home.svg';
import { ReactComponent as SettingSVG } from 'assets/svg/setting.svg';
import { ReactComponent as LogoutSVG } from 'assets/svg/logout.svg';
import { Link } from 'react-router-dom';
interface UserInfo {
accountModalRef?: React.RefObject<HTMLDivElement>;
userName: string; // 이름은 Header의 Prop으로 받아오는 계정 이름을 이용할 예정
}
// onClose의 타입은 테스트가 끝나면 알맞는 바꾸도록하자
const AccountInfoModal = ({ userName, accountModalRef }: UserInfo) => {
return (
<div ref={accountModalRef} className="modal_info_box">
<p className="username text">{userName}</p>
<hr />
<Link to="mypage" className="info_link_area">
<HomeSVG stroke="black" />
<p>마이페이지로 이동</p>
</Link>
<Link to="setting" className="info_link_area">
<SettingSVG stroke="black"/>
<p>계정 설정</p>
</Link>
<Link to="logout" className="info_link_area">
<LogoutSVG stroke="black" />
<p>로그아웃</p>
</Link>
</div>
)
}
export default AccountInfoModal;
모달은 유저의 이름, 마이페이지와 계정설정, 로그아웃으로 연결된 Link 로 구성되어있다.
ref의 경우 모달이외의 영역을 클릭할 시 모달이 닫히는 동작을 하기위해 추가했으며,
위의 컴포넌트에서는 모달의 ref는 prop으로 받아오며
그 이유는 아래의 Header 컴포넌트 코드에서 설명하겠다.
import { forwardRef, useRef, useState } from 'react';
import { Link } from 'react-router-dom';
import useBackGroundClick from 'hooks/useBackgroundClick';
import {ReactComponent as GamaldaIcon} from 'assets/svg/gamaldaIcon.svg';
import {ReactComponent as UserIcon} from 'assets/svg/user.svg';
import AccountInfoModal from 'components/modules/Modal/AccountInfoModal';
type User = {
// authorized는 api로 가져오는 유저 정보중 하나로 로그인 됨을 나타내줌
// userImg: string; // 추후에 이미지 url을 보내거나 파일자체를 보낼때 사용
authorized: boolean;
userName: string;
}
const Header = ({ authorized, userName }: User) => {
const [IsModalOpen, setModalOpen] = useState(false);
const modalRef = useRef<HTMLDivElement>(null);
const onClickButton = () => {
setModalOpen(!IsModalOpen);
};
useBackGroundClick(modalRef, setModalOpen);
return (
<div className="nav_header">
<div className="nav_header_content">
<Link to="/">
{/* {img==="" ? <GamaldaIcon width='70px' height='70px'/> : 여기에 유저 이미지 나오게끔 하는 컴포넌트 삽입} */}
<GamaldaIcon width='70px' height='70px' />
</Link>
<div className={authorized ? "user_icon" :"login_link"}>
{authorized ?
<div>
<UserIcon width='25px' height='25px' onClick={onClickButton} />
{IsModalOpen && <AccountInfoModal accountModalRef={modalRef} userName={userName} />}
</div>
:
<Link to="/">
<p>로그인</p>
</Link>
}
</div>
</div>
</div>
)
};
export default Header;
위에서 모달에 사용할 ref를 Header에서 선언했다고 말했다.
Header 컴포넌트에 모달이 닫히는 상태가 있기 때문이다.
ref를 모달 컴포넌트에 선언하게 되면 hook으로 작성한 외부 클릭 감지 함수를 사용할 때
ref를 불러오기 힘들기 떄문에 이렇게 작성했다.
이 코드에서 상당히 애를 먹게된 곳이 있는데,
바로 accountModalRef={modalRef} 이 부분이다.
이유는 React컴포넌트에는 ref={} 형식으로 넘기게 된다면 prop이 아니므로 undefined가 되어서 이다.
이 문제는 ref가 아니라 ref123 같은 이름의 prop을 사용하면 문제가 해결되지만
forwardRef를 사용해 React컴포넌트에 ref를 전달할 수 있다니 참고하자.
(참고글 : https://www.daleseo.com/react-forward-ref/)
다음으로 모달 외부 클릭을 감지해 모달의 상태를 변경하는 hook 코드를 보자
import { useEffect } from "react";
/**
* 모달이 on된 상태에서 닫아주는 hook
* @param ref ref.currunt를 넣으면 된다.
* @param modalState boolean 형식으로 된 상태를 변경해주는 set함수를 넣어주면됨
* @returns
*/
const useBackGroundClick = (ref: React.MutableRefObject<any> | null, modalState: React.Dispatch<React.SetStateAction<boolean>>
) => {
// if (ref === null) return false;
useEffect(() => {
const handleClick = (event: any) => {
if (ref === null) {
return false;
}
if (ref?.current && !ref.current.contains(event.target)) {
modalState(false);
}
};
window.addEventListener("mousedown", handleClick);
return () => window.removeEventListener("mousedown", handleClick);
}, [ref, modalState]);
}
export default useBackGroundClick;
이 hook 함수는 첫번째 인자로는 모달의 ref. 즉, 경계의 기준이 되는 컴포넌트의 ref를 넣어준다.
두번째 인자는 모달의 상태를 변경해주는 set 함수를 넣어준다.
이제 동작하는 모습을 보도록하자
드디어 헤더 작업을 마무리 했다...
쉽다면 쉬운 작업을 1주일 씩이나 걸렸다...
모달이라는 것을 처음 접했고 처음 협업툴을 다루기 시작해 속도가 늦게 붙은것 같다...
그래도 지금은 좀 익숙해졌으니 다음 작업은 빠르게 진행할 수 있게 노력해야겠다...
'프로젝트 > 가말다 - 마일스톤' 카테고리의 다른 글
로그아웃 기능 중 쿠키 전달을 위한 fetch의 credentials(내용증명) 옵션 사용 (0) | 2023.04.19 |
---|---|
BE - DB연결 방식 변경 (Prisma -> SQL쿼리) (0) | 2023.04.06 |
모달 제작 -1 (1) | 2023.02.01 |
상단 헤더 제작 (0) | 2023.01.29 |
프로젝트 소개 (0) | 2023.01.24 |