const Modal = ({ isOpen, width, height, children }: props) => {
return (
<ModalBackDrop isOpen={isOpen}>
<ModalContainer isOpen={isOpen} width={width} height={height}>
{children}
</ModalContainer>
</ModalBackDrop>
);
};
const useModal = (initialValue: boolean) => {
const [isOpen, setIsOpen] = useState<boolean>(initialValue);
const handleOpenButtonClick = (): void => {
setIsOpen(true);
};
const handleCloseButtonClick = (): void => {
setIsOpen(false);
};
return { isOpen, handleOpenButtonClick, handleCloseButtonClick };
};
전역 상태
로 관리되어야 함.단계별로
관리해야 하는데, 데이터 구조가 적합하지 않음.✔️ 라우터 내 최상위 컴포넌트
로 ModalContainer를 위치함.
// router.ts
const Router = () => {
return (
<BrowserRouter>
<Header />
<ModalContainer />
<Suspense fallback={<div>...loading</div>}>
<Routes>
...
</Routes>
</Suspense>
</BrowserRouter>
);
};
✔️ 모달을 전역 상태
로 관리하기 위해 recoil atom을 추가함.
// recoil/atoms.ts
type ModalState = {
isOpen: boolean;
height: string;
width: string;
type: string;
page?: number;
};
export const modalState = atom<ModalState>({
key: 'modalState',
default: { isOpen: false, height: '400px', width: '400px', type: '' },
});
‼️ 커스텀 훅
으로 모달 열기/닫기, 사이즈 조절 함수를 추가함.
// hooks/useLoginModa.ts
import { useRecoilState } from 'recoil';
import { modalState } from '../recoil/atoms';
const useLoginModal = () => {
const [modal, setModal] = useRecoilState(modalState); // global state
const { isOpen } = modal;
// !! 모달을 열 때 페이지 유형과 페이지 넘버를 받아옴.
const openModal = (type: string, page: number): void => {
setModal({
...modal,
isOpen: true,
type,
page,
});
};
const closeModal = (): void => {
setModal({
...modal,
isOpen: false,
});
};
const updateModalSize = (width: string, height: string): void => {
setModal({
...modal,
width,
height,
});
};
return { isOpen, modal, openModal, closeModal, updateModalSize };
};
export default useLoginModal;
✔️ Header 컴포넌트에서 모달 오픈
이벤트
<NavItemLi onClick={() => openModal('login', 0)}>
로그인하기
</NavItemLi>
✔️ ModalContainer 컴포넌트에서 페이지 유형
에 따라 컴포넌트 렌더링
// components/ModalContainer.tsx
export default function ModalContainer() {
const { isOpen, width, height, type } = useRecoilValue(modalState);
return isOpen ? (
<BackDrop>
<Container width={width} height={height}>
{type === LOGIN && <LoginModal />}
</Container>
</BackDrop>
) : null;
}
✔️ LoginModal 컴포넌트에서 페이지 넘버
에 따라 창 사이즈를 조절 & 컴포넌트 렌더링
// components/login/LoginModal.tsx
export default function LoginModal() {
const {
modal: { page },
updateModalSize,
} = useLoginModal();
// 페이지에 따라 모달 크기 조절
useEffect(() => {
if (page === 0) {
updateModalSize('41.0625rem', '31.4375rem');
} else if (page === 1) updateModalSize('44.0625rem', '27.25rem');
}, [page]);
if (page === 0) return <LoginPage0 />;
if (page === 1) return <LoginPage1 />;
if (page === 2) return <LoginPage2 />;
if (page === 3) return <LoginPage3 />;
if (page === 4) return <LoginPage4 />;
return <div>modal</div>;
}
Reference