[React] React Portal을 이용한 Modal 구현하기

상현·2023년 11월 23일
2

React

목록 보기
10/24
post-thumbnail

React Portal 이란?

react-dom에서 제공하는 도구로, 자식 노드를 부모 그 바깥의 위치에서 렌더링 시킬 수 있다.
예를 들어 자식 컴포넌트가 부모 컴포넌트 바깥으로 튀어나오는 듯한 화면이 필요할 때 (모달, 툴팁 등) 부모의 CSS등에 의해서 종속되어 있는 경우를 벗어날 수 있게 해주어 렌더링을 편하게 도와준다.

코드로 살펴보자면
root div안에 있는 custom-modal이 마치 modaldiv안에서 나오는 것처럼 할 수 있다는 뜻이다.

<div id ="root">
  <div>
    <div>
      <div class='custom-modal' />
    </div>
  </div>
</div>

<div id="modal">
  <!--    <div class='custom-modal' />-->
</div>

모달 구현 해보기

기본 모달 컴포넌트 생성

const Modal = ({children}) => {
  // 표시 여부를 나타내는 전역 상태 값
  const {show} = useSelector(state => state.modalModule);

  // 모달 바깥쪽 Ref 지정
  const overlayRef = useRef(null);
  const dispatch = useDispatch();

  // 모달 바깥쪽 (Overlay) 클릭하면 모달 닫힘
  const onClickOverlay = e => {
    if (e.target === overlayRef.current) {
      dispatch(hideModal());
    }
  };

  return (
    <>
      {show && (
        <ScModalOverlay onClick={onClickOverlay} ref={overlayRef}>
          // 모달 안에 props로 받은 children 렌더링
          <ScModalContainer>{children}</ScModalContainer>
        </ScModalOverlay>
      )}
    </>
  );
};

const ScModalOverlay = styled(CenterContainer)`
  position: fixed;
  z-index: 100;
  background-color: rgba(255, 255, 255, 0.5);
  height: 100%;
`;

const ScModalContainer = styled.section`
  display: flex;
  justify-content: space-around;
  align-items: center;
  flex-direction: column;
  height: 140px;
  background-color: white;
  border-radius: 15px;
  box-shadow: rgba(0, 0, 0, 0.35) 0 5px 15px;
  padding: 20px;

  p {
    font-size: 1.5rem;
  }

  animation: show-popup 0.3s;

  @keyframes show-popup {
    0% {
      transform: scale(0.7);

      opacity: 0;
    }
    45% {
      transform: scale(1.05);

      opacity: 1;
    }
    80% {
      transform: scale(0.95);
    }
    100% {
      transform: scale(1);
    }
  }
`;

export default Modal;

커스텀 모달 컴포넌트 생성

모달로 띄우고 싶은 컴포넌트를 Modal 컴포넌트로 감싼다. 그리고 그 컴포넌트를 react-domcreatePortal 함수를 사용해 DOM에서 id가 modal인 div밑으로 렌더링 하게 한다.

createPortal(chidlrenComponent, targetElement);

index.html

<body>
  <div id="modal" style='position: fixed'></div>
  <div id="root"></div>
</body>
const FollowListModal = ({showFollowing, followerList, followingList, setFollowingNumber, setUserInfo}) => {

  return (
    <>
      {showingList &&
        createPortal(
          <Modal>
            {showingList.map(v => (
              <FollowListRow
              />
            ))}
            {showingList.length === 0 && <h1>친구가 없습니다!</h1>}
          </Modal>,
          document.getElementById('modal'),
        )}
    </>
  );
};

export default FollowListModal;

FollowListModal 컴포넌트는 root 밑에 작성했지만, Portal을 통해 실제 DOM에는 id가 modal인 div 안에 렌더링 되었다.

이벤트 버블링

보통 이벤트는 자식에서 부모로 올라가면서 전달된다. React Portal을 사용하면 트리 구조를 벗어나 렌더링 되기 때문에 이벤트 전달이 되지 않을 것 같지만, 본래의 자식 컴포넌트처럼 잘 전달이 된다.

portal이 DOM 트리의 어디에도 존재할 수 있다 하더라도 모든 다른 면에서 일반적인 React 자식처럼 동작합니다.

Portals - React

profile
프론트엔드 개발자

0개의 댓글