개발을 하던 도중.. 기본 confirm이 너무 맘에 들지 않았던 디자이너는
나에게 custom confirm을 디자인해서 주게 된다
디자인은 사진과 같이 간단한 confirm이었고 한 페이지에만 쓰이는 게 아닌 여러 페이지에서 쓰이는 방식의 confirm 이었다
우선 어디서든 열리게 했어야 했고 title, desc, button text 등 props로 넘겨주어 유동적이게 만들어야 했다
그럼 바로 만들어보자
우선 modal을 만들어보자
import { MouseEventHandler, ReactNode } from 'react';
import MiniButton from 'components/common/MiniButton';
import * as S from './style';
type ModalOptionType = 'CONFIRM' | 'ALERT';
interface PropsType {
option: ModalOptionType;
title: string;
content: ReactNode;
closeText: string;
confirmText: string;
handleClose: MouseEventHandler<HTMLButtonElement>;
handleConfirm: MouseEventHandler<HTMLButtonElement>;
}
const Modal = ({
option,
title,
content,
closeText,
confirmText,
handleClose,
handleConfirm,
}: PropsType) => {
return (
<S.BlurBackground>
{option === 'CONFIRM' ? (
<S.Confirm>
<S.ConfirmWrap>
<S.ConfirmTextBox>
<S.ConfirmTitle>{title}</S.ConfirmTitle>
<S.ConfirmContent>{content}</S.ConfirmContent>
</S.ConfirmTextBox>
<S.ConfirmButtonBox>
<MiniButton
option="UNFILLED"
value={closeText}
onClick={handleClose}
/>
<MiniButton
option="FILLED"
value={confirmText}
onClick={handleConfirm}
/>
</S.ConfirmButtonBox>
</S.ConfirmWrap>
</S.Confirm>
) : (
<S.Alert>
<S.AlertWrap>
<S.AlertTextBox>
<S.AlertTitle>{title}</S.AlertTitle>
<S.AlertContent>{content}</S.AlertContent>
</S.AlertTextBox>
<S.AlertButtonBox>
<MiniButton
option="FILLED"
value="확인"
onClick={handleConfirm}
/>
</S.AlertButtonBox>
</S.AlertWrap>
</S.Alert>
)}
</S.BlurBackground>
);
};
export default Modal;
나는 모달이 그냥 confirm만 되게 하는 것이 아닌 props로 옵션을 주어서 alert, confirm 두 개를 만들어 원하는 대로 쓸 수 있게 하였다
그럼 modal을 어떤 식으로 불러오게 되는 것일까?
우선 modal이라는 recoil을 만들어보자
import { ReactNode } from 'react';
import { atom } from 'recoil';
export const modalState = atom<ReactNode>({
key: 'modalState',
default: null,
});
recoil은 ReactNode를 타입으로 주었다 recoil이 왜 필요한지는 아래서 GlobalModal.tsx를 설명하면서 같이 설명하겠다
import { modalState } from 'atoms/modal.atom';
import { useRecoilValue } from 'recoil';
const GlobalModal = () => {
const modal = useRecoilValue(modalState);
const provide = () => {
if (!modal) return null;
return modal;
};
return <>{provide()}</>;
};
export default GlobalModal;
import ReactDOM from 'react-dom/client';
import { QueryClient, QueryClientProvider } from 'react-query';
import GlobalStyled from 'styles/global.style';
import { RecoilRoot } from 'recoil';
import { BrowserRouter } from 'react-router-dom';
import ScrollTop from 'utils/ScrollTop';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import GlobalModal from 'components/common/GlobalModal';
import App from './App';
const queryClient = new QueryClient();
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement,
);
root.render(
<BrowserRouter>
<RecoilRoot>
<QueryClientProvider client={queryClient}>
<App />
<GlobalModal />
<GlobalStyled />
<ScrollTop />
<ToastContainer
autoClose={2000}
limit={5}
pauseOnHover={false}
position="top-right"
pauseOnFocusLoss={false}
/>
</QueryClientProvider>
</RecoilRoot>
</BrowserRouter>,
);
GlobalModal은 index에 다음과 같이 배치하여 modalState를 불러와 값이 null이면 아무것도 return 하지 않고 만약 값이 들어있다면 modal을 띄우게 될 것이다
하지만 modal을 띄우게 되려면 useModal이 필요하다 사용법은 어떻게 되는 것이고 어떻게 만드는 걸까? 자세하게 알려주겠다
import { modalState } from 'atoms/modal.atom';
import { ReactNode } from 'react';
import { useRecoilState } from 'recoil';
export const useModal = () => {
const [modal, setModal] = useRecoilState(modalState);
const openModal = (m: ReactNode) => {
setModal(m);
};
const closeModal = () => {
setModal(null);
};
return { openModal, closeModal, modal };
};
useModal은 다음과 같다 openModal( modal component 자리 )
만약 modal component 자리에 modal component가 들어오게 되면 그것을
recoil로 저장시킬 것이고 저장이 되면 GlobalModal에서 return 해줘서 화면에 띄워주는 것이다
여기까지 custom modal의 기본 세팅이다 이제 직접 써보자
const generateStudents = () => {
const { admissionYear, numberOfStudents } = generateStudentsData;
const grade = new Date().getFullYear() + 1 - admissionYear;
if (new Date().getFullYear() < admissionYear) {
toast.error('입학년도를 다시 한번 확인해주세요');
return;
}
openModal(
<Modal
option="CONFIRM"
title="학생 아이디 생성"
content={
<p>
{grade}학년 학생 {numberOfStudents}명의 아이디 생성이 맞는지
<br /> 다시 한번 확인해주세요
</p>
}
closeText="취소"
confirmText="생성"
handleClose={closeModal}
handleConfirm={generate}
/>,
);
};
generateStudents를 onclick={generateStudents}와 같이 넣게 된다면
클릭하였을 때 openModal에 component가 들어가게 될 것이고 modal이 이쁘게
사진과 같이 열릴 것이다
custom modal 만들고 디자이너를 만족시키자!
globalModal에 있는 친구는 함수로 빼는것보다 그냥 && 걸어주거나 삼항으로 처리하는게 더 이해하기 쉬울 것 같기도 합니다~ 어떻게 생각하시는지 궁금해요!!!