본문 바로가기
React

[React + Recoil] 재사용 전역 모달 만들기

by LasBe 2023. 2. 6.
반응형

⚡️ Recoil로 전역 모달 만들기


리액트로 모달 만들기 귀찮으신 분들은 다음 글 참고하셔서 라이브러리로 순식간에 모달 구현 해보세요!

[React] 세상 쉬운 리액트 모달 라이브러리


첫 프로젝트에서는 아래 코드와 같이 각각의 모달마다 상태와 추가 코드를 작성했습니다.

const [ modal1, setModal1 ] = useState({onOff: false})
const [ modal2, setModal2 ] = useState({onOff: false})

return (
  <>
    { modal1.onOff && <Modal1 /> }
    { modal2.onOff && <Modal2 /> }
  </>
)

이와 같이 코드를 작성하다보니 초기에는 괜찮았지만
모달의 개수가 많아질수록 관리해야 할 상태들과 추가 코드들이 점차 불어나 관리가 힘들 지경이 되었습니다.

 

프로젝트에 잡혀있는 구조에 따라 무지성으로 페이지를 만들다보니
어느순간 현재 전역 상태 관리 라이브러리로 사용하고 있는 리코일을 이용한다면
AlertModalconsole.log()alert()과 같이 선언적으로, 정말 쉽게 사용할 수 있겠다고 느꼈습니다.

 

그럼 직접 느낀점을 바탕으로 어떻게 만들어봤는지 소개하겠습니다.

 


📌 사용 라이브러리

  • Recoil
  • styled-components

📌 폴더 구조

특이점으론 styled-components 라이브러리 관련 파일은 styleds 폴더에 넣어두었습니다.


📌 전역 상태

type ModalType = {
  isOpen: boolean;
  title: string;
  content: JSX.Element | string;
  callBack?: () => any;
};

export const modalState = atom<ModalType>({
  key: "modalState",
  default: {
    isOpen: false,
    title: "",
    content: ""
  }
});

전역 상태는 정말 간단합니다.

모달을 열고 닫는 것은 isOpen:boolean 으로,
타이틀은 title로,
본문은 간단한 alert으로도 간단히 이용될 가능성이 있기 때문에

string과 확장성을 생각해 JSX.Element까지 타입으로 정의했습니다.


마지막으로 확인 버튼을 눌렀을 때 로직을 처리할 수 있는 callback 함수가 있습니다.


📌 useModal() Hook

type OpenModalType = {
  title: string;
  content: JSX.Element | string;
  callback?: () => any;
};

export const useModal = () => {
  const [modalDataState, setModalDataState] = useRecoilState(modalState);

  const closeModal = useCallback(
    () =>
      setModalDataState((prev) => {
        return { ...prev, isOpen: false };
      }),
    [setModalDataState]
  );

  const openModal = useCallback(
    ({ title, content, callback }: OpenModalType) =>
      setModalDataState({
        isOpen: true,
        title: title,
        content: content,
        callBack: callback
      }),
    [setModalDataState]
  );

  return { modalDataState, closeModal, openModal };
};

모달과 관련된 로직을 useModal 훅으로 분리했습니다.

 

덕분에 모달을 사용하고 싶은 페이지가 있을 땐 반복되는 코드를 작성하지 않고,
훅에서 openModal을 꺼내와 사용하기만 하면 됩니다.


📌 Style

import styled from "styled-components";

export const ModalDimmer = styled.div`
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.4);
  display: flex;
  justify-content: center;
  align-items: center;
`;
export const ModalBody = styled.div`
  display: flex;
  flex-direction: column;
  max-width: 800px;
  max-height: 600px;
  min-width: 300px;
  min-height: 100px;
  background-color: #ffffff;
  border: 1px solid #cbcbcb;
  border-radius: 10px;
`;
export const ModalTitle = styled.div`
  padding: 1rem;
  font-weight: bold;
  font-size: large;
  border-bottom: 1px solid #cbcbcb;
`;
export const ModalContents = styled.div`
  padding: 1rem;
  border-bottom: 1px solid #cbcbcb;
`;
export const ModalFooter = styled.div`
  display: flex;
  flex-direction: row;
`;
export const ModalButton = styled.button`
  background: none;
  border: none;
  width: 100%;
  height: 52px;
  font-weight: bold;
  :hover {
    opacity: 50%;
    transition: 0.5s;
  }
`;

export const ModalButtonWithBorder = styled(ModalButton)`
  border-right: 1px solid #cbcbcb;
`;

styled-components 라이브러리를 이용해 디자인을 했으며,
레이아웃은 display : flex를 이용해 잡아주었습니다.


📌 Modal Component

export const Modal = () => {
  const { modalDataState, closeModal } = useModal();

  return (
    <>
      {modalDataState.isOpen && (
        <ModalDimmer> 
          <ModalBody>
            <ModalTitle>{modalDataState.title}</ModalTitle>
            <ModalContents>{modalDataState.content}</ModalContents>
            <ModalFooter>
              <ModalButtonWithBorder onClick={closeModal}>Cancel</ModalButtonWithBorder>
              <ModalButton onClick={modalDataState.callBack}>Ok</ModalButton>
            </ModalFooter>
          </ModalBody>
        </ModalDimmer>
      )}
    </>
  );
};

공통적으로 사용하는 모달 컴포넌트는 위에서 만든 훅에서 데이터와 모달을 닫는 함수를 끌어와 사용만 합니다.

 

그리고 isOpen 함수를 이용해 조건부 렌더링을 걸어주어 화면에 표시 여부를 결정합니다.

 

화면에 표시 여부를 결정하는 것은 isOpen에 따라 display : none을 이용하는 방법을 사용해도 됩니다.

 


📌 Page

function App() {
  const { openModal } = useModal();

  const modalData = {
    title: 'Modal Title',
    content: 'Modal Content',
    callback: () => alert('Modal Callback()'),
  };

  return (
    <div>
      <button onClick={() => openModal(modalData)}>OPEN MODAL</button>
      <Modal />
    </div>
  );
}

export default App;

저는 간단하게 프로젝트를 만든다고 최상위 컴포넌트에 Modal 컴포넌트와 모달을 여는 버튼을 같이 넣었지만
프로젝트 내에서 사용할 때는 Modal 컴포넌트를 최상위 컴포넌트에 박아두고
실제 모달을 사용하는 페이지에서 useModal 훅을 불러와 사용하면 됩니다.


마치며, Recoil을 이용해 정말 쉽고 간단하게 만든 전역 모달이지만
조금만 수정하고 기능을 추가한다면 실제 프로젝트에서 사용해도 되겠다는 생각을 해봅니다.

반응형

댓글


오픈 채팅